Skip to content

Getting started

  1. Install the package.

    Terminal window
    npm install offlibrary

    Requirements: a browser with service workers, the Cache API and IndexedDB (every evergreen browser). Background Fetch is used when present.

  2. 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 support

    No bundler? Load the IIFE build:

    sw.js
    importScripts('https://cdn.jsdelivr.net/npm/offlibrary@0/dist/offlibrary-sw.iife.js');
    offlibrary.offlibrarySW();
  3. 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 used
    ui: { icons: [{ src: '/icons/icon-192.png', sizes: '192x192', type: 'image/png' }] },
    clickUrl: (item) => `/episodes/${item.id}`, // notification tap opens the episode
    },
    },
    });
  4. 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 unknown
    library.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, use await episodes.done(id).

  5. Play it, online or offline, with the original URL. The service worker answers from the device when the file is stored, including Range requests:

    audio.src = ep.enclosure;
  1. Open your app, download a file and wait for complete.
  2. In DevTools → Application → Cache Storage, you’ll see a cache named offlibrary:<namespace>:<collection> with the file under its original URL.
  3. Switch DevTools → Network to Offline, reload, and play the file. Seeking works because the service worker answers Range with 206.

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);