vite-plugin-offline
v0.0.0
Published
Zero-config offline support and update awareness for Vite apps
Maintainers
Readme
vite-plugin-offline
Small, framework-agnostic offline support for Vite apps. Load the app once, reopen it without a network connection, and tell users when a newer build is ready.
It precaches the complete production build, serves navigations network-first, and exposes typed connectivity and update state. It has no runtime dependencies and does not turn your app into an installable PWA.
Install
pnpm add -D vite-plugin-offline// vite.config.ts
import { defineConfig } from "vite";
import offlineFirst from "vite-plugin-offline";
export default defineConfig({
plugins: [offlineFirst()],
});Build and serve the production output, visit it once online, then it can reopen offline:
pnpm exec vite build
pnpm exec vite previewService workers require HTTPS in production. localhost is allowed for development and previews. The plugin intentionally does not register a service worker in Vite's development server, where persistent caches tend to make HMR confusing.
Know when the app is offline or outdated
The plugin registers the service worker automatically. Import the client helpers anywhere in your app to drive UI or disable network-only features:
import {
activateOfflineFirstUpdate,
subscribeToOfflineFirst,
} from "vite-plugin-offline/client";
const unsubscribe = subscribeToOfflineFirst((state) => {
offlineBanner.hidden = state.isOnline;
updateButton.hidden = !state.isUpdateAvailable;
readyBadge.hidden = !state.isOfflineReady;
});
updateButton.addEventListener("click", () => {
// Activates the waiting build and reloads once it controls the page.
void activateOfflineFirstUpdate();
});The state contains:
isOnline: the browser's connectivity signal. Use it as a UI hint, not proof that a particular API is reachable.isOfflineReady: the app shell has been installed and can be reopened offline.isUpdateAvailable: a newer build is waiting. Prompting is the safe default because an automatic reload can discard form or application state.
For React, subscribe in an effect and keep the snapshot in state:
import { useEffect, useState } from "react";
import {
getOfflineFirstState,
subscribeToOfflineFirst,
} from "vite-plugin-offline/client";
export function ConnectionStatus() {
const [status, setStatus] = useState(getOfflineFirstState);
useEffect(() => subscribeToOfflineFirst(setStatus), []);
if (!status.isOnline) return <p>You are offline. Network features are paused.</p>;
if (status.isUpdateAvailable) return <p>A newer version is ready.</p>;
return null;
}subscribeToOfflineFirst returns an unsubscribe function. You can also call checkForOfflineFirstUpdate() manually or listen for the offline-first:update window event.
Caching behavior
- Every emitted JS, CSS, HTML, and asset file is precached atomically.
- Files copied from Vite's
public/directory are precached by default. - Navigations are network-first, then fall back to the cached URL or
index.html. - Same-origin images, fonts, scripts, styles, media, manifests, and workers fetched at runtime are network-first cached.
- API requests, non-GET requests, and cross-origin requests are not cached by default.
- New builds wait until the user activates them. Old caches are removed only inside this plugin instance's namespace.
This keeps a static breath pacer, calculator, reference tool, or similar client-side app available after its first successful load. Server data is a separate concern: store user data in IndexedDB or another local database and design an explicit synchronization strategy if that data must also work offline.
Options
offlineFirst({
cacheName: "my-app",
precacheAssets: ["extra-data.json"],
excludeAssets: [/\.mp4$/],
precachePublicAssets: true,
runtimeCaching: "assets", // "assets" | "same-origin" | false
navigateFallback: "index.html", // or false for no app-shell fallback
enableUpdateCheck: true,
updateEventName: "offline-first:update",
serviceWorkerFileName: "sw.js",
});Relative asset paths follow Vite's configured base, so deployments under paths such as /tools/breath/ work. An absolute CDN base is rejected because a service worker cannot control another origin.
Use runtimeCaching: "same-origin" only when caching every successful same-origin GET response—including API responses—is appropriate for the app.
Scope
This project is for apps that want focused offline app-shell caching and a tiny update API. If you also need a web app manifest, install prompts, icon generation, advanced Workbox recipes, background sync, or push notifications, use vite-plugin-pwa instead.
Browser support
The plugin targets modern browsers with service worker, Cache Storage, modules, and async function support. Service worker support is the meaningful compatibility boundary; unsupported browsers keep working online without offline features.
License
MIT
