Setting up a Service Worker with Hugo
Setting up a Service Worker with Hugo
Static sites are fast, we all generally agree to that, but they could be faster… One way we can achieve this is through a good service worker led caching strategy, for example, pre-fetching resources that the user is likely to need in the near future, such as the next few pictures in a photo album.
In this post we’ll walk through how to build out a basic service worker for a Hugo website (which is largely similar to the use of service workers in any other context).
Working 9 to 5
If you’re unfamiliar with service workers, check out Mozilla’s service worker documentation at https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API. In brief, a service worker is a proxy that lives between the page and the network, most often a background script that supports an offline first experience. That said, service workers can neither directly interact with the page nor directly access the DOM as it runs on a separate thread; however, a service worker can communicate with pages through messages (I.e. post messages).
In this post, we’ll create a basic service worker with two caches, a static cache we can manually fill with assets that we don’t want to wait for and a runtime cache to pick up assets captured through navigation.
Make the caches and put in the stuffs
To get started we’ll need to create a new file at the root of our site. For the purposes of this walkthrough, we’ll call it service-worker.js. A service worker needs to be in the root of your site in order to do its thing. For Hugo, you’ll want to place service-worker.js in your /static folder.
Once you have service-worker.js, the first thing we’ll need to do is create our static cache (precache) which we’ll fill with the assets we want immediately available. To do this copy the code below into your service-worker.js.
|
|
const version
sets the version of the precache. You can change the version to remove the previous cache from the client.
install
is the service worker’s install event, installation is attempted when a downloaded file is found to be new.
return cache.addAll
holds the precache items. As a best practice, the static cache should be limited to the static assets you wish to cache, usually comprised of static, uniquely definable assets such as those in your /static folder (i.e. JavaScript, stylesheets, images, etc.). In the snippet above, we’re adding “index.html” to the static cache as well as including an alias “./” for index.html.
In addition, we’re adding an offline page to the cache to handle conditions where the cache cannot be called.
While tempting, but not recommended, it’s possible to fill the cache with all of your posts/content. You simply need to generate a list of relative urls for all of your pages. With Hugo it’s easy to range over a collection of pages in a section, for example, posts. The script below is a simple example of ranging over all the content in posts and generating a list of relative URIs that can be used for the purposes of precaching.
|
|
You can see how this works here. In this example I’m prepending and appending each URI with ’ and ‘, respectively which makes it easy to copy and paste as-is into the service worker snippet above.
Activate
The next step in our service worker’s lifecycle is activation (through an activate event). Activation is generally a good time to clean up old caches and other things associated with the previous version of your service worker.
Here we’ll do exactly that, manage our caches. In the steps above we initialized and populated our static cache with static assets that are necessary for our site. Now we’ll activate both our static cache and a new dynamic cache that we’ll fill through navigation events as “runtime” as well as cleanup old caches.
|
|
Go fetch, good doggo
The fetch event is where all the heavy lifting is. This is where we pull from the static cache and both retrieve and add content to the runtime cache ‘return cache.put(event.request, response.clone()).then(()’ as one or more pages is navigated. The offline page is triggered in our catch and in the event it is unavailable, we fallback again with a Response object.
|
|
Your completed service-worker.js should now look as follows:
|
|
Now that we have our service worker in place, we’ll need to register it. Our registration will need to be available to all pages on the site. As a common practice the registration event can be included in the sites’ footer.
|
|
That’s pretty much all we need to do for a basic service worker. To test if it’s working, run hugo server
or your preferred build command and browse to your site. Open dev tools and check the console to see if your service worker was successfully registered.
If it has been, open Applications (in Chromium) and validate that there are two caches available, your precache and runtime caches. The precache should be populated with the assets you specified in the install event and the runtime cache filled as you navigate around your site.
If you want to see if offlining is working, click Network and set the value to offline. Now visit a page that you haven’t visited before to ensure it’s not being served from either of the existing caches.
Resources
To learn more about the general concepts around service workers, check out Google’s PWA labs at https://github.com/google-developer-training/pwa-training-labs.
Comments
Comments
John M.
Thu, 08 Dec. 2022, 23:35 UTC
Super helpful! Glad I found this!