---
title: "Notify users about new deployments with Next.js + SWR"
url: https://monogram.io/blog/notify-users-about-new-deployments-with-next-js-swr
description: "Keep your users informed about new features and bug fixes by ensuring that they always use the latest version of your app."
date: 2023-01-16
authors: ["Leonardo"]
---

# Notify users about new deployments with Next.js + SWR

**Published:** Jan 16, 2023 | **Authors:** Leonardo, Senior Engineer | **Categories:** Engineering

---

Keep your users informed about new features and bug fixes by ensuring that they always use the latest version of your app.

Notifying users about new deployments helps users stay informed about new features and bug fixes, and it can also help improve the user experience by ensuring that users are always using the latest version of the app. In this article, we will discuss how to use [Next.js](https://nextjs.org/) and [SWR](https://swr.vercel.app/) to notify users about new deployments.

*In a rush?* [*Check the full source code here*](https://github.com/monogramdesign/notify-new-deployments)

## Version

To store a version, we'll save a JSON file containing the timestamp in the [public](https://nextjs.org/docs/basic-features/static-file-serving) folder. The version will be saved before each build with a prebuild script, so that we can read that file in the app to tell which version the app is using when opened.

```
// scripts/create-version-json.mjs

import { writeFile } from 'fs/promises'

async function createVersionJson() {
	const versionJson = {
		timestamp: Date.now()
	}

	const filePath = join(__dirname, '../public/version.json');

	try {
		await writeFile(filePath, JSON.stringify(versionJson), {
			encoding: 'utf8',
			flag: 'w+' //  open the file for reading and writing, positioning the
			//             stream at the beginning of the file. The file is
			//             created if it does not exist.
		})

		console.log(`✅ Version timestamp is now ${versionJson.timestamp}`)
	} catch (error) {
		console.error('❌ Error creating file:\n', error)
	}
}

// call the function so that it can be executed as a node script
createVersionJson()
```

[*Check the source code here*](https://github.com/monogramdesign/notify-new-deployments/blob/main/scripts/create-version-json.mjs)

## Prebuild script

To store the version JSON before every build, we'll need a prebuild script. We'll also add a predev script so that the same notification also works in development.

```
// package.json

{
...
	"scripts": {
		...
		"predev": "node scripts/create-version-json.mjs",
		"dev": "next dev",

		"prebuild": "node scripts/create-version-json.mjs",
		"build": "next build",
		...
	}
...
}
```

*ℹ️ You can check the* [*npm documentation*](https://docs.npmjs.com/cli/v9/using-npm/scripts#pre--post-scripts) *if you want to know more about pre/post scripts.*

*Check the* [*whole package.json content here*](https://github.com/monogramdesign/notify-new-deployments/blob/main/package.json)

## Using SWR to cache the current version

Now let's create a custom hook to store the version timestamp into an immutable SWR hook:

```
// lib/usePageLoadVersion.ts

import useSWRImmutable from 'swr/immutable'

import versionJSON from '../public/version.json'

export default function usePageLoadVersion() {
	const { data, isLoading } = useSWRImmutable('pageLoadVersion', () => versionJSON.timestamp)

	return { pageLoadVersion: data, isLoading }
}
```

[*Check the source code here*](https://github.com/monogramdesign/notify-new-deployments/blob/main/lib/usePageLoadVersion.ts)

With this hook we cache the current version from version.json on page load, or as soon as it runs.

## Using SWR to watch for new versions

Now let's create another hook to get the latest version from version.json.

Here we'll fetch the latest version.json. When a there is a new version, the timestamp will be different than the immutable one. That way we know the app has a new version.

```
// lib/useLatestVersion.ts

import useSWR from 'swr'

export function useLatestVersion() {
	const { data, isLoading } = useSWR('latestVersion', fetchLatestVersion, {
		revalidateOnFocus: true,
		revalidateIfStale: true,
		revalidateOnReconnect: true,
		
		// we don't need to check on mount as the user has just entered the page
		revalidateOnMount: false
	})

	return { latestVersion: data, isLoading }
}

async function fetchLatestVersion() {
	const response = await fetch('/version.json', {
		headers: {
			'cache-control': 'no-cache'
		}
	})

	const version = await response.json()

	return version.timestamp as number
}
```

[*Check the source code here*](https://github.com/monogramdesign/notify-new-deployments/blob/main/lib/useLatestVersion.ts)

## Comparing the latest version with the cached one

Now that we have both versions, we can compare them to check whether the app has a new version. As a proof of concept, let's show a button to the user to reload the page whenever the versions differs. That way the user will get the latest and greatest version of your app.

```
// components/NotifyNewVersion.tsx

export default function NotifyNewVersion() {
	const { latestVersion, isLoading: isLoadingLatest } = useLatestVersion()
	const { pageLoadVersion, isLoading: isLoadingPageLoad } = usePageLoadVersion()

	const isLoading = isLoadingLatest || isLoadingPageLoad

	if (isLoading) {
		return <span>Loading...</span>
	}

	// latestVersion and pageLoadVersion will be undefined while loading,
	// so we check if both are truthy before comparing their values
	const hasNewVersion = Boolean(latestVersion && pageLoadVersion)
		? latestVersion !== pageLoadVersion
		: false	

	if(!hasNewVersion) {
	  return null
	}

	return (
		<>
			<span>
				The app has a new version!
			</span>

			<button 
				onClick={() => window.location.reload()} 
				type="button"
			>
				Click here to update
			</button>
		</>
	)
}
```

[*Check the source code here*](https://github.com/monogramdesign/notify-new-deployments/blob/main/components/NotifyNewVersion.tsx)*. It differs a bit from the code shown above so that the example app makes more sense, but the functionality is the same.*

## Cool, but where should I put it?

Now that we have the hooks and the component, it's just a matter of where do you want to notify your users.

You may want to notify only signed-in users if your app requires authentication, or only dynamic pages if you have certain pages that are fully static. In those cases you could put the component directly in the page:

```
// pages/your-page.tsx

import NotifyNewVersion from '../components/NotifyNewVersion'

export default function PageWhereUsersWillBeNotified() {
	return (
		<main>
			<NotifyNewVersion />
		</main>
	)
}

```

In case you want the component to be rendered in all pages, you can put it into the [custom app](https://nextjs.org/docs/advanced-features/custom-app):

```
// pages/_app.tsx

import type { AppProps } from 'next/app'

import NotifyNewVersion from '../components/NotifyNewVersion'

import '../styles/globals.css'

export default function App({ Component, pageProps }: AppProps) {
	return (
		<>
			<NotifyNewVersion />

			<Component {...pageProps} />
		</>
	)
}

```

You can also use [the new Next.js layouts feature](https://beta.nextjs.org/docs/routing/pages-and-layouts#layouts) to share the component between multiple pages. For that to work we'll need to make a little change in the NotifyNewVersion component to indicate it's a [client component](https://beta.nextjs.org/docs/rendering/server-and-client-components#client-components):

```
'use client' // <-- just add this line

// ... the rest of the component stays the same as before:

export default function NotifyNewVersion() {
	const { latestVersion, isLoading: isLoadingLatest } = useLatestVersion()
	const { pageLoadVersion, isLoading: isLoadingPageLoad } = usePageLoadVersion()

	const isLoading = isLoadingLatest || isLoadingPageLoad

	if (isLoading) {
		return <span>Loading...</span>
	}

	// latestVersion and pageLoadVersion will be undefined while loading,
	// so we check if both are truthy before comparing their values
	const hasNewVersion = Boolean(latestVersion && pageLoadVersion)
		? latestVersion !== pageLoadVersion
		: false	

	if(!hasNewVersion) {
	  return null
	}

	return (
		<>
			<span>
				The app has a new version!
			</span>

			<button 
				onClick={() => window.location.reload()} 
				type="button"
			>
				Click here to update
			</button>
		</>
	)
}
```

[*Check the source code here*](https://github.com/monogramdesign/notify-new-deployments/blob/main/components/NotifyNewVersion.tsx)

Then you can also use that component in your layout or any other [server component](https://beta.nextjs.org/docs/rendering/server-and-client-components#server-components) that's under the app directory:

```
// app/app/nested/layout.tsx -- `/app/app` is duplicated in order to have `/app` in the route and easily indicate where you are while testing this

import type { ReactNode } from 'react'
import NotifyNewVersion from '../../../components/NotifyNewVersion'

export default function NestedRouteLayout({ children }: { children: ReactNode }) {
	return (
		<>
			{children}

			<NotifyNewVersion />
		</>
	)
}
```

[*Check the source code here*](https://github.com/monogramdesign/notify-new-deployments/blob/main/app/app/nested/layout.tsx)

## Abstracting hasNewVersion as a custom hook

In order to avoid having to always check for the versions and its loading states as we did in the NotifyNewVersion component, we can also abstract hasNewVersion as a reusable hook useHasNewVersion. It will also be useful if you need to check the app version in more than one place. SWR makes this easy:

```
// lib/useHasNewVersion.ts

import useSWR from 'swr'

import { useLatestVersion } from './useLatestVersion'
import { usePageLoadVersion } from './usePageLoadVersion'

export function useHasNewVersion() {
	const { latestVersion } = useLatestVersion()
	const { pageLoadVersion } = usePageLoadVersion()

	// wait until both versions are available
	const shouldCheck = Boolean(latestVersion && pageLoadVersion)

	const { data, isLoading } = useSWR(
		shouldCheck ? [latestVersion, pageLoadVersion] : null,
		() => latestVersion !== pageLoadVersion
	)

	return { 
		hasNewVersion: data, 
		isLoading: isLoading || shouldCheck === false 
	}
}
```

[*Check the source code here*](https://github.com/monogramdesign/notify-new-deployments/blob/main/lib/useHasNewVersion.ts)

Then the component can just use its value to know what to render:

```
// components/NotifyNewVersionWithHook.tsx

export default function NotifyNewVersionWithHook() {
	const { hasNewVersion, isLoading } = useHasNewVersion()

	if (!hasNewVersion)
		return null

	return (
		<>
			<span className={styles.title}>There is a new version!</span>

			<button 
				className={styles.button} 
				onClick={() => window.location.reload()}
			>
				Click here to update
			</button>
		</>
	)
}
```

[*Check the source code here*](https://github.com/monogramdesign/notify-new-deployments/blob/main/components/NotifyNewVersionWithHook.tsx)

## **Conclusion**

That's it! In this article we provided a way to notify users about new deployments with Next.js and SWR with a few hooks, a script and components to illustrate its usage. Now you can take it and use as you need as the code should be very easy to reuse in any Next.js project.

Don't forget that you can check the [Notify users about new deployments with Next.js + SWR repository](https://github.com/monogramdesign/notify-new-deployments) with a working app containing all the code presented in this article.

## Contact Us

[Build what comes next](https://monogram.io/#contact) | Email: [hello+llm@monogram.io](mailto:hello+llm@monogram.io)

Provide your contact details and claim a time to meet. We'll help you do the rest.

Monogram is an Applied AI Studio based in Atlanta that designs and deploys production AI systems — connecting models, data, workflows, and enterprise software to automate real work.

The contact form collects: name, email, company, estimated budget (USD), a project description, and how you heard about Monogram.

Not sure where to start? Tell us what you're trying to automate or build — we'll help you figure out where AI fits.


---

**More content:** [Home](/.md) | [About](/about.md) | [Work](/work.md) | [Engineering](/engineering.md)

Monogram helps companies, organizations, and firms adopt AI by designing and building proof of concepts (POCs), MVPs, intelligent workflows, and production-ready AI applications.

Website designed and developed by [Monogram](https://monogram.io)