What Is WorkboxWebpackPlugin and How Does Offline Caching Work in Create React App?

WorkboxWebpackPlugin is a webpack integration that generates a service worker to precache build assets and enable offline functionality in Create React App production builds.

Create React App (CRA) leverages the WorkboxWebpackPlugin to transform static builds into Progressive Web Apps capable of operating without network connectivity. In the facebook/create-react-app repository, this plugin is configured to inject a precache manifest directly into your service worker during production builds. Understanding how this integration works allows developers to customize caching strategies while maintaining automatic asset management.

How WorkboxWebpackPlugin Integrates with CRA

The plugin is imported in the main webpack configuration file and conditionally applied only during production builds when a service worker source file exists.

// packages/react-scripts/config/webpack.config.js
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');

Production Build Configuration

When isEnvProduction is true and the service worker source file exists, CRA appends the InjectManifest instance to the webpack plugins array at lines 707-718.

// packages/react-scripts/config/webpack.config.js (lines 707-718)
new WorkboxWebpackPlugin.InjectManifest({
  swSrc,                                   // Path to src/service-worker.js
  dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
  exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/],
  maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
}),

Service Worker Source Resolution

The swSrc variable resolves to the developer-provided service worker entry point via the paths configuration in packages/react-scripts/config/paths.js at line 79.

// packages/react-scripts/config/paths.js (line 79)
swSrc: resolveModule(resolveApp, 'src/service-worker'),

The Offline Caching Mechanism

Workbox operates through two distinct phases: build-time manifest generation and runtime cache management.

Precache Manifest Injection

The InjectManifest plugin scans all emitted webpack assets, generates a manifest of URLs with their content hashes, and replaces the self.__WB_MANIFEST placeholder in your source file. This list includes all static assets required for your application's shell, ensuring the app loads instantly on repeat visits.

Runtime Caching Strategies

Beyond static precaching, the service worker intercepts fetch events to handle dynamic content. Developers can define custom strategies such as StaleWhileRevalidate for API calls or CacheFirst for images, which Workbox applies at runtime based on the routes you register.

Build Output and Registration

After processing, the augmented service worker is emitted to build/service-worker.js. When a user visits the deployed application, the browser registers this script, downloads the precached assets, and subsequent requests are served from the cache even when the device is offline.

Implementing Offline Caching in Your Project

CRA provides a PWA template that demonstrates standard patterns, but you can extend these configurations to meet specific requirements.

Default PWA Template Structure

When you initialize a project with the PWA template, src/service-worker.js contains the following standard implementation:

// src/service-worker.js
/* eslint-disable no-restricted-globals */
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate } from 'workbox-strategies';

// Precache all assets generated by the build step.
precacheAndRoute(self.__WB_MANIFEST);

// Cache API calls with a stale-while-revalidate strategy.
registerRoute(
  ({ url }) => url.origin === self.location.origin && url.pathname.startsWith('/api/'),
  new StaleWhileRevalidate()
);

Custom Runtime Caching Rules

Because CRA uses InjectManifest, you can add complex caching logic alongside the generated precache list without interfering with the build process:

// src/service-worker.js (additional snippet)
import { CacheFirst } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';

// Cache images with expiration
registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({
    cacheName: 'images',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 60,
        maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
      }),
    ],
  })
);

Disabling the Service Worker

To remove offline capabilities entirely, delete src/service-worker.js or ensure the file does not exist at the resolved path. When swSrc is missing, CRA skips the Workbox plugin entirely, producing a standard static build without service worker registration.

Key Source Files in the CRA Repository

These files orchestrate the offline caching behavior:

File Purpose Location
webpack.config.js Configures WorkboxWebpackPlugin.InjectManifest for production packages/react-scripts/config/webpack.config.js
paths.js Resolves the absolute path to src/service-worker.js packages/react-scripts/config/paths.js
noopServiceWorkerMiddleware.js Provides a development-time no-op service worker to prevent caching issues packages/react-dev-utils/noopServiceWorkerMiddleware.js

Summary

  • WorkboxWebpackPlugin.InjectManifest is added to webpack only during production builds when src/service-worker.js exists, as configured in packages/react-scripts/config/webpack.config.js.
  • The plugin replaces self.__WB_MANIFEST with a generated list of hashed asset URLs, enabling automatic precaching of your build output.
  • Offline caching works by registering the generated service worker, which serves precached assets from the Cache Storage API and can apply custom runtime strategies for dynamic content.
  • Developers maintain full control over caching behavior by editing src/service-worker.js while CRA handles the manifest generation and build integration.

Frequently Asked Questions

What is the difference between InjectManifest and GenerateSW in Workbox?

InjectManifest allows you to write your own service worker code while Workbox injects the precache manifest, whereas GenerateSW creates the entire service worker automatically without custom code. CRA uses InjectManifest to give developers control over runtime caching strategies and custom logic while still automating the precache list generation.

How do I update the cached assets when deploying a new version?

Each production build generates new content hashes for changed assets. When the browser detects a different service worker byte-to-byte, it installs the new version and activates it after all tabs close, updating the precache manifest automatically through Workbox's built-in lifecycle management.

Can I use WorkboxWebpackPlugin in development mode?

No, CRA intentionally excludes the plugin from development builds to prevent caching issues during active development. The noopServiceWorkerMiddleware.js utility serves a no-op service worker instead, ensuring development always reflects your latest code changes without stale cache interference.

Why is my service worker not caching API responses?

By default, CRA's template only precaches static build assets defined in self.__WB_MANIFEST. You must explicitly add runtime caching routes using registerRoute() with appropriate strategies like StaleWhileRevalidate or NetworkFirst in your src/service-worker.js file to cache API calls or other dynamic resources.

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 →