Introduction
In this tutorial, I solve the problem that users use an old version of your web app. This might happen, because they don't realize a new version of your app was released. I introduce a simple way to notify users based on the npm package version. A React app will be used as an example, but at the end of the tutorial, you should be able to use the principles with any npm-based Single Page Application.
Prerequisites
- Basic JavaScript/ReactJS knowledge
- Basic npm knowledge
- Tiny bit of bash knowledge
- npm and git
Step 1 - Project setup
Note: You can also use an existing project that was set up with create-react-app
and skip this step
In this first step, we set up the project with create-react-app. Running npx create-react-app version-check-example --use-npm
will create a folder version-check-example
with all necessary React dependencies and npm configurations.
It will also initialize a git repository, and an npm project.
Step 2 - Make the app version available to our app at runtime
Our source of truth for the version of our app is the "version"
field in the package.json
file.
By default, it is "version": "0.1.0"
and will be incremented while you are developing your app.
At build time, npm will inject an environment variable called $npm_package_version
into the build process, which we sadly cannot use directly in our React app. To make the value of this variable available at run time, we create a .env
file in the project folder and add the following line:
REACT_APP_VERSION=$npm_package_version
All variables starting with REACT_APP
will be injected by webpack, so we can use it at run time like this:
const version = process.env.REACT_APP_VERSION
Step 3 - (Periodic) Version checking
In this step, we will create a script that can be periodically called to fetch the newest version from the server and compare it to the current client version.
First we will create a version file with a static version string. We can fetch this version in our app to compare it to the client side version. We will generate this file in the next step.
For now, just create a file named version
in the public
directory and add the following content (the current version):
0.1.0
Important Do not add a new line or any spaces to this file. It should exactly contain the version.
Next, create a file called version-check.js
in the src/utils
directory, with the following content:
// fetch current app version from the server and return it as a string
const fetchVersion = async () => {
const response = await fetch('/version')
if (!response.ok) {
throw new Error('Error fetching version')
}
return response.text()
}
export const checkForNewVersion = async () => {
// fetch version from server
let serverVersion
try {
serverVersion = await fetchVersion()
} catch (error) {
console.error(error)
return
}
// compare server version to running version
// you can add your own logic to test if the client version is actually older than the client version
// to prevent problems when your /version endpoint delivers an old version by mistake (e.g. when it is cached)
if (process.env.REACT_APP_VERSION === serverVersion) {
return
}
// show alert
window.alert('A new app version is available. Please reload the page.')
}
Note: create-react-app
automatically serves files from the public folder. That's why we can just load the version file with fetch('/version)
. You might have to make adjustment for your production build. You can read more about this here.
The function checkForNewVersion
can now be called periodically anywhere in your app, e.g. in the src/App.js
file:
import React, { useEffect } from 'react'
import checkForNewVersion from './utils/checkForNewVersion'
export const App = () => {
// when component mounts, set an interval that checks for a new version every 5 minutes
useEffect(() => {
const interval = setInterval(checkForNewVersion, 5 * 60 * 1000) // check every 5 minutes
// clear interval when component unmounts
return () => clearInterval(interval)
}, [])
return (
<div>...</div>
)
}
If you now run the app with npm run start
and change the content of the public/version
file to 0.1.1
, an alert should show 5 minutes after opening the app.
Step 4 - Generate version file based on package version
npm allows us to manage our package version. You can bump the version by using
npm version major
npm version minor
npm version patch
which will update the version in the package.json
according to the semantic versioning rules.
Whenever npm version
runs, some scripts that you can specify in your package.json
file will also be executed in the following order:
- preversion
- version
- postversion
We now create a bash script, which will read the $npm_package_version
environment variable and write it to the public/version
file. The script is located in the scripts
directory and is called version.sh
echo -n $npm_package_version > public/version
git add public/version
- The first line writes the value of the
$npm_package_version
environment variable to thepublic/version
file. - The second line adds the changed file to git, so that it will be included in the git tag created by npm
After that, we add the following line to the "scripts"
section of our package.json
:
"version": "bash ./scripts/version.sh"
Note: You might need to make the version.sh
file executable, e.g. by running chmod +x scripts/version.sh
Step 5 - Test
Run npm version patch
in your command line. This should update the "version"
field in your package.json
(and package-lock.json
) from 0.1.0
to 0.1.1
. The content of the file public/version
should now also be 0.1.1
.
If you run git log
in your command line, there should be an entry looking something like this:
commit your-commit-hash (HEAD -> master, tag: v0.1.1)
...
Conclusion
We achieved to create an automatic process that notifies users when a new app version is available. Whenever we update the version of the app with npm version [major/minor/patch]
, a script will run, that automatically updates the version file, adds it to git and creates a git tag.
The app periodically fetches the version endpoint and compares it to the version that is currently running in the browser. If the version changed, we can notify the user and tell him to reload the page.