DEV Community

Justin Poehnelt
Justin Poehnelt

Posted on • Originally published at justin.poehnelt.com on

Eleventy Progressive Web App

Lighthouse Obsession

I have caught the Eleventy Lighthouse obsession bug! Most of the pages on this site score “Four Hundos” with Lighthouse, but I only recently added a service worker to get an installable Progressive Web App (PWA).

Progressive Web Apps (PWAs) are web apps that use service workers, manifests, and other web-platform features in combination with progressive enhancement to give users an experience on par with native apps.

Some Code

The code is fairly simple, especially with the eleventy.after event. Basically, there are two parts:

  1. Register the worker in your page.
  2. Use workbox-build to generate the service worker.

Register Worker

The following snippet should be added to your base layout or template.

<script>
    if ('serviceWorker' in navigator) {
        navigator.serviceWorker.register('/sw.js');
    }
</script>
Enter fullscreen mode Exit fullscreen mode

Note the path used is /sw.js. This needs to match below!

Generate Service Worker

Install the workbox-build package.

The workbox-build module integrates into a node-based build process and can generate an entire service worker, or just generate a list of assets to precache that could be used within an existing service worker.

npm i -D workbox-build
Enter fullscreen mode Exit fullscreen mode

The following should be added to your eleventy.js config file. Check out all the options for generateSW.

// eleventy.js
const workbox = require("workbox-build");

module.exports = (eleventyConfig) => {

    eleventyConfig.on('eleventy.after', async () => {
        // see https://developer.chrome.com/docs/workbox/reference/workbox-build/#type-GenerateSWOptions
        const options = {
            cacheId: 'sw',
            skipWaiting: true,
            clientsClaim: true,
            swDest: `public/sw.js`,  // TODO change public to match your dir.output
            globDirectory: 'public',  // TODO change public to match your dir.output
            globPatterns: [
                '**/*.{html,css,js,mjs,map,jpg,png,gif,webp,ico,svg,woff2,woff,eot,ttf,otf,ttc,json}',
            ],
            runtimeCaching: [
                {
                urlPattern: /^.*\.(html|jpg|png|gif|webp|ico|svg|woff2|woff|eot|ttf|otf|ttc|json)$/,
                handler: `StaleWhileRevalidate`,
                },
            ],
        };

        await workbox.generateSW(options);
    });

    return {
        dir: {
            output: "public", // TODO update this
        },
    };
}
Enter fullscreen mode Exit fullscreen mode

There are some old plugins for Eleventy to help with this, although I don’t really see the value. And they use hacky solutions that predate the eleventy.after hook.

Fix your web manifest

I said there were only two steps, but there is a third! You will need a Web Manifest. This is very custom to your site. I use Eleventy to generate from a JavaScript template using global data values.

Perfect results

And here it is!

Perfect Lighthouse score with PWA

Perfect Lighthouse score with PWA

The service worker preloading and caching almost the entire site makes for an amazingly responsive experience. The obsession is worth it!

Top comments (0)