Getting started
-
Install the package.
Terminal window npm install offlibraryTerminal window pnpm add offlibraryTerminal window yarn add offlibraryRequirements: a browser with service workers, the Cache API and IndexedDB (every evergreen browser). Background Fetch is used when present.
-
Add Offlibrary to your service worker. Pick your path:
A service worker at the root of your site, bundled by Vite, webpack, esbuild or similar:
sw.js import { offlibrarySW } from 'offlibrary/sw';offlibrarySW(); // Background Fetch handling + serving stored files with Range supportNo bundler? Load the IIFE build:
sw.js importScripts('https://cdn.jsdelivr.net/npm/offlibrary@0/dist/offlibrary-sw.iife.js');offlibrary.offlibrarySW();Add three lines to your Workbox service worker (
injectManifest):sw.js import { precacheAndRoute } from 'workbox-precaching';import { registerRoute } from 'workbox-routing';import { NetworkOnly } from 'workbox-strategies';import { initOfflibrary, offlibraryMatch, offlibraryHandler } from 'offlibrary/workbox';initOfflibrary(); // Background Fetch handling, no fetch listenerprecacheAndRoute(self.__WB_MANIFEST); // your app shell, as beforeregisterRoute(offlibraryMatch, offlibraryHandler({ fallback: new NetworkOnly() }));No extra dependency:
offlibrary/workboximports nothing from Workbox. Setup forworkbox-swand Vite PWA, the plugin for existing strategies, and more: Offlibrary for Workbox. -
Create the library in your page and describe your collections:
library.js import { createOfflibrary } from 'offlibrary';navigator.serviceWorker.register('/sw.js');export const library = createOfflibrary({namespace: 'podcast',collections: {episodes: {maxItems: 20, // keep the 20 most recently usedui: { icons: [{ src: '/icons/icon-192.png', sizes: '192x192', type: 'image/png' }] },clickUrl: (item) => `/episodes/${item.id}`, // notification tap opens the episode},},}); -
Download, and show progress.
import { library } from './library.js';const episodes = library.collection('episodes');library.on('progress', ({ item }) => render(item)); // item.progress is 0..1, or null if the size is unknownlibrary.on('complete', ({ item }) => toast(`${item.title} is ready offline`));library.on('error', ({ item, error }) => console.warn(item.id, error.code));downloadButton.onclick = () =>episodes.download({ id: ep.guid, url: ep.enclosure, title: ep.title, size: ep.length });download()resolves once the download has started. To wait for the end, useawait episodes.done(id). -
Play it, online or offline, with the original URL. The service worker answers from the device when the file is stored, including
Rangerequests:audio.src = ep.enclosure;
Check it works
Section titled “Check it works”- Open your app, download a file and wait for
complete. - In DevTools → Application → Cache Storage, you’ll see a cache named
offlibrary:<namespace>:<collection>with the file under its original URL. - Switch DevTools → Network to Offline, reload, and play the file. Seeking works because the service worker answers
Rangewith206.
Without a service worker
Section titled “Without a service worker”Offlibrary still downloads (with the fetch transport), tracks progress and manages storage. Read stored files yourself:
const blob = await episodes.blob('ep-42');audio.src = URL.createObjectURL(blob);- Collections and strategies: the model behind the config.
- Configuration reference: every option.
- Build a podcast app: a complete walk-through.