Drupalwoo ☉

How to Build a Progressive Web App with Drupal

A Progressive Web App (PWA) combines the reach of a website with capabilities commonly associated with native mobile applications. Visitors can load it from a browser, install it on a phone or desktop, and continue using selected features when the network is unreliable. Drupal is well suited to this model because it already provides structured content, user permissions, routing, caching, and a flexible theming system.

The most reliable approach is to treat the PWA as an enhancement to a well-built Drupal website rather than as a replacement for its server-rendered functionality. Begin with a fast, accessible site, then add a web app manifest, a service worker, offline behaviour, and optional features such as install prompts or push notifications. This approach works for an Australian publisher, retailer, community organisation, or government-facing site serving users from Sydney to regional Queensland.

Define The PWA Requirements

A PWA should solve a clear user problem. A news site may allow visitors to read recently loaded articles on a flight, while a retailer may cache product pages and preserve a shopping basket during a short connection failure. A council or community website might make venue details, event information, and emergency contacts available offline.

Drupal content does not automatically become available without an internet connection. Decide which routes have value offline and which must always contact the server. Public articles, documentation, event details, and product information are good candidates. Administration pages, personalised dashboards, checkout forms, and pages containing private data generally should not be cached.

Consider the network conditions of your audience before selecting a caching strategy. Australian visitors on the NBN may have a fast home connection, but mobile users on regional roads, at festivals, or in remote areas can experience high latency and interrupted coverage. An offline-first design should preserve useful content without attempting to copy the whole website onto a device.

Prepare Drupal For A Progressive Web App

Start with a current, supported version of Drupal, a responsive theme, and a valid HTTPS configuration. Service workers are restricted to secure contexts, with localhost being the main development exception. Confirm that every canonical page, asset, and API endpoint uses HTTPS before testing installation or offline caching.

Performance work should happen before PWA features are added. Enable Drupal’s internal page and dynamic page caching where appropriate, configure asset aggregation, compress images, and review third-party scripts. A service worker can make repeat visits faster, but it cannot compensate for oversized JavaScript, slow database queries, or a theme that blocks rendering.

A custom Drupal module is often the cleanest place for PWA integration. It can attach the manifest link, add the service worker registration script, provide the worker file from the correct domain, and expose configuration through Drupal’s administration UI. A contributed PWA module may reduce development time, but check its Drupal core compatibility, maintenance activity, cache behaviour, and support for Drupal 10 or 11 before adopting it.

Add The Web App Manifest

The web app manifest is a JSON file that describes how the browser should treat the site when a visitor installs it. It normally includes the application name, a shorter name for the launcher, the start URL, display mode, theme colour, background colour, and icons. Link it from the page head using a Drupal library or theme attachment rather than inserting it inconsistently into individual templates.

A basic manifest might look like this:

{
  "name": "Example Drupal Service",
  "short_name": "Example",
  "start_url": "/?source=pwa",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#005a9c",
  "icons": [
    {
      "src": "/themes/custom/example/images/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/themes/custom/example/images/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

Use square PNG or WebP assets at suitable resolutions, and include maskable artwork if the design supports adaptive Android icons. The start_url should lead to a useful, publicly accessible page. Avoid directing every installed visitor to a login form or a route that requires JavaScript before displaying content.

Set the manifest MIME type to application/manifest+json, confirm it is reachable without authentication, and check that the icon paths are not blocked by server rules. Branding should remain recognisable when the application appears beside familiar apps used by Australian customers, such as banking, transport, or supermarket applications.

Implement Service Worker Caching

A service worker is a JavaScript file that runs separately from the page and can intercept network requests. It has installation and activation stages, then handles fetch events. A simple implementation can precache an offline page and serve static assets from the browser cache:

const CACHE_NAME = 'drupal-pwa-v1';
const OFFLINE_URL = '/offline';

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME).then(cache => cache.add(OFFLINE_URL))
  );
  self.skipWaiting();
});

self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(keys =>
      Promise.all(
        keys
          .filter(key => key !== CACHE_NAME)
          .map(key => caches.delete(key))
      )
    )
  );
  self.clients.claim();
});

self.addEventListener('fetch', event => {
  if (event.request.method !== 'GET') return;

  event.respondWith(
    fetch(event.request).catch(() =>
      caches.match(event.request).then(response =>
        response || caches.match(OFFLINE_URL)
      )
    )
  );
});

This example is deliberately limited. A production site needs a deliberate strategy for HTML, images, CSS, JavaScript, JSON, and third-party resources. Cache-first is useful for versioned static assets. Network-first is often safer for changing Drupal pages, because it attempts to show fresh content before falling back to a cached response. Stale-while-revalidate can show a cached page immediately while updating it in the background.

Resource Recommended strategy Reason
Versioned CSS and JavaScript Cache first Filenames can change when assets are rebuilt
Public article HTML Network first Visitors should receive current published content
Images and fonts Cache first with limits Repeat visits benefit from local copies
Search and filtered results Network only Results depend on current requests and parameters
Forms, checkout, and account pages Network only Avoid storing sensitive or state-changing data
Offline fallback page Precache It must work when no request can reach Drupal

Place the service worker at the site root, such as /service-worker.js, if it needs to control the whole domain. A worker located under /modules/custom/example/js/ normally has a narrower scope. Register it from a Drupal-attached JavaScript library after checking browser support:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js');
  });
}

Be careful with Drupal’s cache metadata. Cached HTML can contain personalised blocks, session information, language variations, or content that has just been unpublished. Do not use a broad service-worker rule to cache every response. Exclude authenticated users and sensitive paths, and use cache versioning so old assets are removed after each deployment.

Design A Useful Offline Experience

An offline page should explain the situation clearly and provide something useful. It can include navigation to previously opened content, contact details, opening hours, or a small set of key instructions. A blank browser error is a poor experience for someone checking a venue address in Melbourne or trying to access an event schedule on a train with an intermittent connection.

For content-heavy websites, the service worker can cache recently visited pages after a successful network response. Add limits for the number of HTML documents, images, and API responses stored locally. A cache eviction policy prevents the application from consuming excessive device storage, which matters on lower-cost phones and shared family devices.

Forms require a separate design. A contact form or registration form should not appear to submit while the device is offline unless the application stores the data securely and has a clear synchronisation process. For a simple Drupal implementation, disable submission offline and display a message explaining that a connection is required. If offline form submission is essential, use encrypted client-side storage, explicit consent, conflict handling, and careful review under the Australian Privacy Act.

Add Installation And Engagement Features

Browsers decide when to show an installation prompt, and many users will never install a website. Provide a clear value exchange: faster repeat access, saved reading, local event information, or a more convenient full-screen interface. Do not display an intrusive banner immediately after page load. A small, dismissible prompt shown after meaningful engagement is usually more effective.

The beforeinstallprompt event can support a custom install button in compatible Chromium-based browsers. Safari on iPhone and iPad uses a different installation flow, so provide simple instructions for adding the site to the Home Screen. Test the experience on current Android and iOS devices rather than assuming that a desktop browser reflects mobile behaviour.

Practical engagement features include:

Push notifications need a server-side delivery design, permission management, and unsubscribe controls. They also introduce privacy, operational, and content-governance responsibilities. A local Australian retailer should avoid sending repeated promotional alerts, while an emergency information service must have a reliable fallback channel because a browser notification is not guaranteed to arrive.

Test Deploy And Maintain The PWA

Test the application in normal, slow, and disconnected conditions. Chrome DevTools can simulate offline mode and inspect service-worker registrations, storage, manifest errors, and cache contents. Lighthouse provides useful checks for installability and performance, but it should support real device testing rather than replace it.

Release testing should cover the following areas:

Use a staging domain with HTTPS and realistic cache headers before publishing the worker on the production domain. A service worker can continue serving old files after a deployment, so increment the cache name, remove obsolete entries during activation, and provide a controlled update path. Avoid skipWaiting() if an immediate update could leave an open page using old HTML with new JavaScript; choose the update behaviour deliberately.

Monitor service-worker errors, failed asset requests, installation rates, and offline fallback usage. Drupal deployments should include the manifest, icons, worker, and registration library in version control. When editors unpublish an article, remember that a previously cached copy may remain on a device until its cache expires or is explicitly removed. A documented cache policy keeps content, privacy, and maintenance requirements aligned.

A Drupal PWA succeeds when its offline features are selective, its cached content is trustworthy, and its installation experience is unobtrusive. Build the foundation around Drupal’s existing security and caching systems, then add progressive enhancements that remain useful on every supported browser and connection.