⚙️ Offline-capable Astro apps via SWSR (Service Worker Side Rendering)

Overview

Astro-service-worker

⚙️ Offline-capable Astro apps via SWSR (Service Worker Side Rendering)

astro-service-worker will take your Astro SSR project, and create a service worker build out of it. This has several benefits:

  • Your app is now offline-capable
  • Your app is now installable
  • The function invocations of your hosting provider are reduced dramatically, because requests can be served by the service worker in-browser
  • Huge performance benefits
  • It's a progressive enhancement

All you have to do is add the integration, and consider that the code you write in your Astro frontmatter will now also need to run in the browser/service-worker. This means that you will not be able to make use of Nodejs built-in dependencies, or other commonjs libraries. If you still want to write server-only code, you can use the networkOnly configuration option.

Usage

Install:

npm i -S astro-service-worker

Add the integration to your configuration:

astro.config.mjs:

import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify';
import serviceWorker from 'astro-service-worker';

export default defineConfig({
  adapter: netlify(),
  integrations: [
    serviceWorker()
  ]
});

Note: astro-service-worker requires your app to run in SSR mode, instead of SSG mode.

Configuration

export default defineConfig({
  integrations: [
    serviceWorker({
      /** Provide custom service worker logic */
      swSrc: 'user-sw.js',

      /** 
       * Excludes specific pages from the service worker bundle, and forces them to always go to the network
       * This is useful for server-only specific code, for example database connections
       */
      networkOnly: ['/networkonly-astro'],

      /** Configure workbox options */
      workbox: {},

      /** Both default to true, useful if you want to provide a custom installation experience */
      skipWaiting: false,
      clientsClaim: false,

      /** Configure esbuild options */
      esbuild: {},

      /** Enables minifcation for esbuild, defaults to true */
      minify: false,

      /** Override the default service worker registration and update script */
      swScript: '',

      /**
       * Provide a module specifier to a custom shim file. This may be useful when integrating third party
       * SSR integrations, which may need to shim certain API's in a service worker environment
       */
      shim: [
        // local module
        `${process.cwd()}/custom-shim.js`,
        // bare module specifier
        '@worker-tools/location-polyfill'
      ],

      /** 
       * Defaults to true, can be set to false when run in a non-browser environment, like for example a cloudflare worker
       * in which case it'll skip precaching the workbox manifest
       */
      browser: false,
    }),
  ]
});

Overwriting Workbox options

Internally, astro-service-worker makes use of Workbox's injectManifest functionality. You can overwrite the default configuration via the workbox options:

export default defineConfig({
  integrations: [
    serviceWorker({
      workbox: {
        globPatterns: ['**/*.{js,css,html,png,jpeg}'],
      }
    }),
  ]
});

Adding custom Service Worker logic

It could be the case that you need to extend the Service Worker to add custom logic. To do this, you can use the swSrc option.

export default defineConfig({
  integrations: [
    serviceWorker({
      swSrc: 'my-custom-sw.js',
    }),
  ]
});

my-project/my-custom-sw.js:

self.addEventListener('fetch', (e) => {
  console.log('Custom logic!');
});

Note that you can also use modules in your custom service worker logic:

import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate } from 'workbox-strategies';

registerRoute(
  /^https:\/\/fonts\.googleapis\.com/,
  new StaleWhileRevalidate({
    cacheName: 'google-fonts-stylesheets',
  })
);

Combine with other integrations

You can also combine this integration with other integrations.

import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify';
import customElements from 'custom-elements-ssr/astro.js';
import serviceWorker from './index.js';

export default defineConfig({
  adapter: netlify(),
  integrations: [
    customElements(),
    serviceWorker()
  ]
});

Shim

It could be the case that other integrations will need to shim certain API's in the service worker, however. In this case, you can provide a custom import. The imports you provide here will be put at the very top of the service worker module before bundling.

import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify';
import serviceWorker from './index.js';

export default defineConfig({
  adapter: netlify(),
  integrations: [
    serviceWorker({
      shim: [
        // local module
        `${process.cwd()}/custom-shim.js`,
        // bare module specifier
        '@worker-tools/location-polyfill'
      ]
    })
  ]
});

Network-only

It could be the case that you would like to make use of some server-only endpoints or pages, perhaps for creating database connections, or other things that depend on Nodejs built-in modules that are not available in the browser. If that is the case, you can specify which page you'd like to exclude from the service worker bundle:

export default defineConfig({
  integrations: [
    serviceWorker({
      networkOnly: ['/networkonly-page', '/db-endpoint', 'etc']
    }),
  ]
});

Future: Streaming astro apps

In the future, once Astro release streaming responses, we can make use of that to improve performance even further:

/blog/[id].astro:

---
import Header from '../src/components/Header.astro';
import Sidemenu from '../src/components/Sidemenu.astro';
import Footer from '../src/components/Footer.astro';
const { id } = Astro.params;
---
<html>
  <Header/>
  <Sidemenu/>
  {fetch(`/blog/${id}.html`).then(render)}
  <Footer/>
html>

In a similar fashion to this Workbox example:

about`, ({event}) => apiStrategy.handle({ event: event, request: new Request('/content/foo.md'), }), () => caches.match(FOOTER_CACHE_KEY, {cacheName: CACHE_NAME}), ]); registerRoute('/foo', streamResponse);">
import { strategy } from 'workbox-streams';
import { registerRoute } from 'workbox-core';

const streamResponse = strategy([
  () => caches.match(HEADER_CACHE_KEY, {cacheName: CACHE_NAME}),
  () => ``,
  ({event}) => apiStrategy.handle({
    event: event,
    request: new Request('/content/foo.md'),
  }),
  () => caches.match(FOOTER_CACHE_KEY, {cacheName: CACHE_NAME}),
]);

registerRoute('/foo', streamResponse);

As Alex Russell says:

This is awesome because it means that you can now get the document starting to request your (SW cached) CSS, JS, and other "header" resources in parallel with SW startup and the network fetch. None of the steps serialise until content comes back.

Given that the Astro's render function is a tagged template literal which returns an Astro component, which is an async Iterable, it seems like this future may not be far off:

class AstroComponent {
  constructor(htmlParts, expressions) {
    this.htmlParts = htmlParts;
    this.expressions = expressions;
  }
  get [Symbol.toStringTag]() {
    return "AstroComponent";
  }
  *[Symbol.iterator]() {
    const { htmlParts, expressions } = this;
    for (let i = 0; i < htmlParts.length; i++) {
      const html = htmlParts[i];
      const expression = expressions[i];
      yield markHTMLString(html);
      yield _render(expression);
    }
  }
}
You might also like...

Adds clap button (like medium) to any page for your Next.js apps.

Adds clap button (like medium) to any page for your Next.js apps.

@upstash/claps Add a claps button (like medium) to any page for your Next.js apps. Nothing to maintain, it is completely serverless 💯 Check out the d

Nov 23, 2022

Examples of using various CSS-in-JS libs in Astro (repo for withastro/astro#4432)

astro + css-in-js This monorepo will show examples of various CSS-in-JS libraries in Astro. Currently only shows a basic counter example, would be nic

Dec 18, 2022

A javascript REST ORM that is offline and real-time capable

A javascript REST ORM that is offline and real-time capable

Rekord Rekord is an ORM - a way to define properties and relationships - that interacts with local storage, a RESTful service, and a real-time service

Oct 18, 2022

This is a demo for Remix SPA's via Service Worker. (doesn't currently work in Safari)

Welcome to Remix! Remix Docs Development From your terminal: npm run dev This starts your app in development mode, rebuilding assets on file changes.

Oct 2, 2022

Offline rendering examples at the command line with Node.js

Elementary Audio Offline Rendering Examples This repository holds a set of small examples using Elementary in Node.js to process audio files. Each sub

Jun 12, 2022

React-app - Building volume rendering web app with VTK.js,react & HTML Using datasets provided in vtk examples (head for surface rendering and chest for ray casting)

SBE306 Assignment 4 (VTK) data : Directory containing Head and Ankle datasets Description A 3D medical viewer built with vtk-js Team Team Name : team-

Jul 19, 2022

Multithread emulator. The wrun allows you to dynamically run a function inside a Web Worker on the client side, without the needing of a dedicated file

wrun This lib allows you to dynamically run a function inside a Web Worker on the client side, without the needing of a dedicated file. This means tha

Nov 5, 2022

Making service workers easy so that your app is fast and reliable, even offline.

tulo.js Making service workers easy to use so that your app can be fast and reliable, even offline. Welcome to tulo.js, a service worker library that

Nov 16, 2022

Redirect requests of current origin to another domain with Service Worker.

Service Worker to Redirect Origin This is a tool for your static website which could intercept all GET requests of the origin domain and redirect them

Aug 28, 2022

A service worker that buffers a full video, so when the video tag ask for ranges, these can be satisfied. Play + pause = buffer the whole video.

Full Video Buffer with Service Workers The specification of the preload attribute on a Video element won't allow you to fully buffer a video. This rep

Nov 2, 2022

A common front-end/Service Worker-based Key/Value database based on CacheStorage

Cache-DB A common front-end/Service Worker-based Key/Value database based on CacheStorage const db = new CacheDB('ChenYFanDB') undefined await d

Sep 30, 2022

Persistent key/value data storage for your Browser and/or PWA, promisified, including file support and service worker support, all with IndexedDB. Perfectly suitable for your next (PWA) app.

Persistent key/value data storage for your Browser and/or PWA, promisified, including file support and service worker support, all with IndexedDB. Perfectly suitable for your next (PWA) app.

BrowstorJS 🚀 💾 🔒 Persistent key/value data storage for your Browser and/or PWA, promisified, including file support and service worker support, all

Aug 5, 2022

An upgradable boilerplate for Progressive web applications (PWA) with server side rendering, build with SEO in mind and achieving max page speed and optimized user experience.

An upgradable boilerplate for Progressive web applications (PWA) with server side rendering, build with SEO in mind and achieving max page speed and optimized user experience.

React PWA v2 A highly scalable, Progressive Web Application foundation,boilerplate, with the best Developer Experience. Demo: https://demo.reactpwa.co

Dec 26, 2022

Framework-agnostic CSS-in-JS with support for server-side rendering, browser prefixing, and minimum CSS generation

Aphrodite Framework-agnostic CSS-in-JS with support for server-side rendering, browser prefixing, and minimum CSS generation. Support for colocating y

Jan 1, 2023

Web component server-side rendering

🌊 Ocean Web component HTML rendering that includes: Rendering to Declarative Shadow DOM, requiring no JavaScript in the client. Automatic inclusion o

Dec 16, 2022

A JSDOM alternative with support for server side rendering of web components

A JSDOM alternative with support for server side rendering of web components

About A JSDOM alternative with support for server side rendering of web components. Happy DOM aim to support the most common functionality of a web br

Dec 30, 2022

Authenticated server-side rendering with Nuxt 3 and Firebase 9

Authenticated server-side rendering with Nuxt 3 and Firebase 9.

Dec 23, 2022

Server-side rendering blog runs on Cloudflare workers

Serverside rendered blog I have tried something completely against to current web trends. What are these? I am using the React as backend framework wi

Jun 24, 2022
Comments
  • investigate if check for `event.request.mode` is needed

    investigate if check for `event.request.mode` is needed

    https://github.com/thepassle/astro-service-worker/blob/33b5d6d6394b4b476b9fa51202dd3107a6d77267/service-worker-integration/service-worker-entrypoint.js#L25

    opened by thepassle 0
  • Cannot read properties of undefined (reading 'renderers')

    Cannot read properties of undefined (reading 'renderers')

    Hi, I'm trying to use your integration, but I get this error:

    Cannot read properties of undefined (reading 'renderers')
      File:
        C:\Users\danyn\Documents\GitHub\explorershandbook\node_modules\astro-service-worker\index.js:27:33
      Code:
          26 |       'astro:config:setup': async ({ config, command, injectScript }) => {
        > 27 |         renderers = config._ctx.renderers;
             |                                 ^
          28 |         cfg = config;
          30 |         /** Add SW registration script */
    
    opened by zAlweNy26 0
Owner
Pascal Schilp
Front-end Developer at ING
Pascal Schilp
Redirect requests of current origin to another domain with Service Worker.

Service Worker to Redirect Origin This is a tool for your static website which could intercept all GET requests of the origin domain and redirect them

Menci 9 Aug 28, 2022
A tiny utility to asynchronously drive a namespace exposed through a Worker.

proxied-worker Social Media Photo by Ricardo Gomez Angel on Unsplash A tiny utility to asynchronously drive a namespace exposed through a Shared/Servi

Andrea Giammarchi 43 Dec 8, 2022
Easily redirect one entire domain to another with a serverless Cloudflare Worker.

Domain Redirecting with Cloudflare Workers Easily redirect one entire domain to another with a serverless Cloudflare Worker. All paths and other data

Erisa A 19 Dec 11, 2022
A tool library for handling window && iframe && worker communication based on the JSON RPC specification

rpc-shooter A tool library for handling window && iframe && worker communication based on the JSON RPC specification 一个基于 JSON-RPC 规范用于处理 window && if

臼犀 89 Dec 20, 2022
Cloudflare Worker that will allow you to progressively migrate files from an S3-compatible object store to Cloudflare R2.

A Cloudflare Worker for Progressive S3 to R2 Blog Post: https://kian.org.uk/progressive-s3-to-cloudflare-r2-migration-using-workers/ This is a Cloudfl

Kian 29 Dec 30, 2022
Send emails using Cloudflare Worker, for free.

Email API for Proselog. Not intended for use outside of Proselog, but it should work with any worker, without any configuration. import { sendEmail }

Proselog 65 Nov 7, 2022
Cloudflare Worker to make a R2 Bucket public!

r2-public-worker A Cloudflare Worker to make your R2 bucket public! Minimum Requirements Cloudflare Account wrangler >= 2.0.2 Note: Ensure you are usi

Cole Mackenzie 20 Sep 19, 2022
Type-safe and Promisified API for Web Worker and Iframe

?? You can help the author become a full-time open-source maintainer by sponsoring him on GitHub. typed-worker Install npm i typed-worker Usage Create

EGOIST 189 Dec 31, 2022
Using Cloudflare worker to generate host list from firebog to keep updated.

AdGuardCloudflareHostGenerator Use a cloudflare worker to generate a up to date list from FireBog's ticked list found at https://v.firebog.net/hosts/l

Jake Steele 14 Nov 30, 2022
Example repo for getting NextJS, Rust via wasm-pack, and web workers all playing nicely together.

Example of integrating WASM & web workers with a Typescript NextJS project. Running yarn yarn dev Open localhost:3000 Layout Rust code is in ./rust, g

Studio Lagier 63 Dec 23, 2022