How next-pwa Handles Service Worker Generation in LunaTV

LunaTV automates service worker generation by configuring the next-pwa plugin in next.config.js to emit a Workbox-powered sw.js file into the public directory, auto-register it at runtime, and force immediate activation via skipWaiting.

LunaTV is a Next.js application developed by MoonTechLab that leverages the next-pwa plugin to deliver Progressive Web App capabilities without manual service worker code. The project handles service worker generation entirely through build-time configuration, utilizing Workbox to precache static assets and manage runtime caching strategies. This approach eliminates the need for custom Service Worker files while ensuring reliable offline performance.

Configuration in next.config.js

The service worker generation process starts in next.config.js at lines 72-77, where the next-pwa plugin is instantiated with a concise set of options that control the entire lifecycle.

// next.config.js – PWA setup
const withPWA = require('next-pwa')({
  dest: 'public',                     // ⟶ sw.js → /public/sw.js
  disable: process.env.NODE_ENV === 'development',
  register: true,                     // auto‑register on page load
  skipWaiting: true,                  // immediate activation
});
module.exports = withPWA({
  // …other Next.js config
});

Four critical parameters dictate how the service worker is produced:

  • dest: "public" – Directs next-pwa to emit the generated sw.js file into the public/ folder, making it automatically available at the root URL (/sw.js).
  • disable – Set to process.env.NODE_ENV === 'development', this prevents service worker creation during local development to avoid caching issues.
  • register: true – Injects a small runtime script that calls navigator.serviceWorker.register('/sw.js') automatically when pages load.
  • skipWaiting: true – Calls skipWaiting() inside the worker, allowing newly installed versions to activate immediately rather than waiting for existing tabs to close.

Build-Time Generation Process

When you run next build, next-pwa uses Workbox under the hood to generate the service worker dynamically. According to the LunaTV source code, this process follows three distinct stages.

First, the plugin collects all static assets—including JavaScript bundles, CSS files, and images—from the build output. Next, it creates a precache manifest that lists each asset alongside a hash-based revision string for cache-busting. Finally, it generates the sw.js file in the public/ directory containing the Workbox runtime.

The generated worker automatically handles:

  1. Precaching of all manifest assets during the install event.
  2. Runtime caching strategies with a default fallback to NetworkFirst for uncached requests.
  3. Immediate activation via skipWaiting() when updates are detected.

Runtime Registration

Because register: true is set in the configuration, LunaTV does not require manual service worker registration code. The plugin automatically injects a lightweight client-side snippet into the HTML that executes:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js');
}

Developers can verify activation by accessing the registration object:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.ready.then(registration => {
    console.log('SW active, scope:', registration.scope);
  });
}

This ensures the browser picks up the generated worker immediately upon page load without additional developer intervention.

Customizing Caching Strategies

While LunaTV uses sensible defaults, developers can extend the Workbox configuration by passing a runtimeCaching array to next-pwa. This allows customization of caching behavior for specific URL patterns beyond the automatic precaching.

const withPWA = require('next-pwa')({
  dest: 'public',
  runtimeCaching: [
    {
      urlPattern: /^https:\/\/api\.example\.com\//,
      handler: 'NetworkFirst',
      options: {
        cacheName: 'api-cache',
        expiration: { maxEntries: 50, maxAgeSeconds: 300 },
      },
    },
  ],
});

This configuration creates a dedicated cache for API responses with specific expiration limits, supplementing the automatically generated precache manifest.

Summary

  • next-pwa is configured in next.config.js (lines 72-77) to handle service worker generation automatically.
  • The dest: 'public' option outputs sw.js to the public folder, serving it from the site root.
  • Workbox generates a precache manifest at build time containing all static assets with revision hashes.
  • register: true injects automatic client-side registration, eliminating the need for manual navigator.serviceWorker code.
  • skipWaiting: true forces immediate service worker activation, ensuring users receive updates without closing tabs.

Frequently Asked Questions

Where is the service worker file located in LunaTV?

The generated service worker file is located at public/sw.js in the build output. Because the dest option is set to "public" in next.config.js, next-pwa emits the file directly into this directory, making it accessible at the root URL path /sw.js.

How does LunaTV disable PWA features during development?

LunaTV checks process.env.NODE_ENV in the disable option within next.config.js. When the environment equals "development", next-pwa skips service worker generation entirely, preventing caching issues that could interfere with hot reloading and debugging.

Can I customize caching behavior in LunaTV's PWA setup?

Yes, you can extend the default Workbox configuration by adding a runtimeCaching array to the next-pwa options in next.config.js. This allows you to define specific caching strategies, cache names, and expiration policies for dynamic content like API responses.

What triggers service worker updates in LunaTV?

Service worker updates are triggered whenever the build process generates a new precache manifest with different revision hashes for static assets. Because skipWaiting is set to true, the new worker activates immediately during the next page load, replacing the previous version without requiring users to close existing tabs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →