@sentinel-js/react
v0.1.6
Published
A lightweight React SDK that polls for application updates and handles them gracefully. It supports a ready-made toast banner, **silent updates** (reload on next navigation with no UI), and a headless API for custom UIs.
Readme
@sentinel-js/react
A lightweight React SDK that polls for application updates and handles them gracefully. It supports a ready-made toast banner, silent updates (reload on next navigation with no UI), and a headless API for custom UIs.
Important: This package is designed to work with
@sentinel-js/vite-plugin. The plugin injects the current build version and writes aversion.jsonfile; without it, the SDK will report version as"unknown"and log a console warning.
Installation
npm install @sentinel-js/react
# or
pnpm add @sentinel-js/reactPeer dependencies: react and react-dom (>=16.8.0). You must have them installed in your app.
When you add @sentinel-js/vite-plugin to your Vite config, it automatically sets resolve.dedupe and optimizeDeps.include so a single React instance is used in both dev and build. No extra config is required.
Usage
Option A: Standard UI (Toast Banner)
The simplest setup: add <SentinelToast /> and it will poll for updates and show a "New version available" banner with a "Refresh" button.
import { SentinelToast } from '@sentinel-js/react';
function App() {
return (
<div>
<AppRoutes />
<SentinelToast style={{ right: '20px', bottom: '20px' }} />
</div>
);
}The toast is fixed at bottom-right by default; use style or className to customize.
Option B: Silent Update (Zero UI)
No banner: the SDK waits for a client-side route change (e.g. user clicks a link), then performs a full page reload so the next view is the new version. Good for apps where you want updates without interrupting the user.
import { useSentinel } from '@sentinel-js/react';
function App() {
useSentinel({
silent: true,
pollingInterval: 15000, // Check every 15 seconds
});
return <AppRoutes />;
}Option C: Custom UI (Headless)
Use the hook and build your own notification (e.g. with react-hot-toast, sonner, or a custom modal).
import { useSentinel } from '@sentinel-js/react';
function App() {
const { hasUpdate, reload, currentVersion, remoteVersion } = useSentinel({
onUpdateAvailable: (version) => console.log('New version:', version),
});
if (hasUpdate) {
return (
<div className="update-banner">
New version available! (current: {currentVersion}, new: {remoteVersion})
<button onClick={reload}>Refresh now</button>
</div>
);
}
return <AppRoutes />;
}API Reference
useSentinel(config?)
Hook that polls the version file, compares with the current build version, and exposes update state and a reload function.
Returns:
| Property | Type | Description |
|----------|------|-------------|
| hasUpdate | boolean | true when a newer version has been detected. |
| currentVersion | string | Version of the current build (injected by the Vite plugin, or 'unknown' without it). |
| remoteVersion | string \| null | Version string from the server, or null before first successful check. |
| reload | () => void | Calls window.location.reload(). Use after showing your own "Refresh" UI. |
| checkNow | () => Promise<void> | Manually trigger a version check. |
Configuration (shared by useSentinel and <SentinelToast />)
| Prop | Type | Default | Description |
|------|------|--------|-------------|
| pollingInterval | number | 60000 | How often (in ms) to poll the version file. |
| versionFileUrl | string | '/version.json' | URL path to the version file (same as generated by the Vite plugin). |
| silent | boolean | false | If true, no UI is shown; reload happens on next route change (when an update was detected). |
| checkOnFocus | boolean | true | Run a version check when the window gains focus. |
| onUpdateAvailable | (version: string) => void | undefined | Callback when a new version is detected (e.g. for analytics or custom UI). |
<SentinelToast /> (UI-only props)
In addition to the config props above, the toast component accepts:
| Prop | Type | Description |
|------|------|-------------|
| style | React.CSSProperties | Override default positioning and styling. |
| className | string | Additional CSS class names. |
If silent is true, <SentinelToast /> renders nothing.
How it works
- Build time:
@sentinel-js/vite-plugininjects a global__SENTINEL_VERSION__(a short hash) and writesversion.json(e.g. indist/) with the same version. - Runtime: The SDK reads
__SENTINEL_VERSION__as the current version and periodically fetchesversionFileUrl(with a cache-busting query param). - Update detection: When the remote version differs from the current one, the SDK sets
hasUpdate: trueand either shows the toast (default) or waits for a route change to reload (silent mode).
Troubleshooting
"Current version is unknown. Ensure @sentinel/vite-plugin is installed."
The app was built without@sentinel-js/vite-plugin, or the plugin’sdefinedid not run (e.g. wrong hook order). Ensure the plugin is in your Vite config and you run a production build (vite build). See @sentinel-js/vite-plugin.Multiple React instances / "Cannot read properties of null (reading 'useState')"
If you use the built package in an app that also bundles React, ensure a single React instance (e.g. Viteresolve.dedupe: ['react', 'react-dom']). In monorepos, aliasing the library to source in the app’s Vite config can avoid duplicate React.
License
ISC
