December 31, 2019
Chris Ball
Static sites, produced with tools like Gatsby, Hugo, and others, are popular for a reason. They deploy almost anywhere, are extremely fast, and don’t compromise on the developer experience. When paired with a CMS though, new sites must be built each time an editor makes an update in the UI.
‍
This isn’t always a problem since static site rebuilds are fast, but it does mean that that tooling needs to be inserted after changes are published to perform the rebuild. Most CMS providers offer webhooks to power this functionality.
Recently, a number of services that have sprung up recently to offer “preview’ or staging versions of a site, and some that attempt to keep the entire preview client-side as part of the editor. But as good as these are, it does mean that an editor (who is often a non-technical user) needs to sit through a rebuild process to see changes they have made.
‍
Recently, I set out to explore a technique to allow instant updates from a CMS-backed site on staging sites, while keeping the production version static. Let’s dive in and see how that technique works.
‍
‍
‍
After that, set up a Next.js page that fetches data from the CMS using GraphQL.
‍
Reloading the page to see updates works well, but gets tedious for the user that is previewing the changes. What if we could build in a “live reload” feature, that automatically refreshes the staging site after changes are saved? To accomplish a live reload feature, we can:
To set the version header, we will create a hash of the data after it is fetched during Server Side Rendering. We’ll create a utility to keep things clean.
‍
The first hook we will add will simplify the handling of browser focus events.
‍
‍
‍
‍
Here’s how to use the hooks we just put in place:
‍
To recap what is going on:
‍
Note:
This same technique can be leveraged on any Single Page App to notify users that a new version has been released, and that they should reload the page. In that case, you would typically add a confirmation prompt before reloading on their behalf.
‍
Up to this point, we’ve created a Server Rendered Next.js app that will automatically reload on focus if the page data has been updated in our CMS. The final step is to bring the high performance of a static site to our page.
‍
To do that, we will leverage a built in feature built into the Now platform called Serverless Pre-Rendering. Similar to the header approach above, we’re going to set a special cache header to cache our pages at the CDN layer. In practical terms, that means our page will be as fast as a static site for the duration that it is cached.
‍
Now’s CDN will send a cached version of the page to the user. If the cache time has expired, it will asynchronously fetch and re-cache the page using a Serverless lambda in the background. This is awesome, because it avoids the “slow for 1 user every x seconds” problem you encounter with many caching strategies.
‍
Let’s add a util to set the proper cache header during Server Side Rendering.
‍
‍
To use the util, we set a cache time on a per-page basis. For example, we might want our homepage to only be cached for a max of one minute before refetching, but our contact page could be cached significantly longer.
‍
Once you deploy this code to staging, you should be able to verify that a cached response was returned:
Now will intelligently clear caches for you every time you deploy! See https://zeit.co/docs/v2/network/caching/#cache-invalidation for more detail.
‍
What if you can’t or don’t want to host on Now? As I mentioned earlier, Next.js also supports exporting a static site. Using this, we can change our build pipeline to export a static version for production.
‍
Unfortunately, this requires adding some configuration to your app, especially if you have dynamic pages. Here’s an example config that filters out dynamic pages and populates sub-pages based on the results of a GraphQL query:
‍
You could also leverage the custom webhook to run a workflow on GitHub Actions that accomplishes the same thing.
We now have what seems to be the best of both worlds. Our staging site acts like a traditional server that reflects changes as soon as they are saved in the CMS. Due to the CDN caching, our production site is just as fast as a static site, with the added bonus of never being out of date for longer than our cache time. We never need to wait on a site rebuild.
‍
We’re currently rebuilding the Echobind site, which leverages this technique. Look for it to be released soon!
Massive props to the team at ZEIT for not only adding this feature to the Now platform, but also for writing a blog post on Serverless Pre-Rendering that demonstrated the focus reload technique.
‍
Chris is CTO at Echobind, a full-service agency specializing in custom web and mobile apps. When he’s not helping developers grow or creating amazing things for clients, you’re likely to find him playing music, cycling, or camping.