You may have heard things about Gatsby a few years ago, and if you’re not keeping an eye on what we’re doing today, it’s entirely understandable for you to assume nothing has changed.
Naturally, given Gatsby operates in the JavaScript space, things have not only changed in the last few years, but they’ve also likely changed in the last few weeks. This is why I’ve written this article explaining what Gatsby can do today, and how I used some of our new features when upgrading my personal website: paulie.dev from Gatsby 2 to Gatsby 4.
Let’s dive in!
Server-Side Rendering (SSR)
In the fall of 2021, Gatsby launched Gatsby 4 with SSR support. I’ve used this on paulie.dev/dashboard. There are three features on my dashboard that are ideally suited to SSR
Now let’s dive into each of these and how Gatsby 4 enabled me to build them easily!
All Reactions
The reaction data is requested from a Fauna Database, and displayed in an interactive Accordion component. The reactions are first grouped by type (e.g. Happy, Cool, Tongue, and so on) and then listed by slug (URL), and a count is also displayed for each of the grouped properties.
Visitors By Country And Visitors By Location
The Visitors By Country data is requested from the new Google Analytics Data API (GA4) and listed in order by the amount of visits per country. Visitors By Location data is requested from the soon-to-be deprecated Google Analytics Core Reporting API (UA), and each location is plotted around a 3D globe that I created using three.js / @react-three/fiber. It’s made interactive using Orbit Controls from @react-three/drei. You might be wondering why I didn’t plot the GA4 data around the globe, too. The lat/long required to plot the points isn’t part of the new GA4 API. See for yourself in the GA4 Dimensions & Metrics Explorer (Built with Gatsby)
Here’s an issue on the ga-dev-tools GitHub Repo… fingers crossed it’ll get looked into at some point. 🤞
The data displayed in these two features is up to date as of the last time the page loaded. The Visitors By Country data will also be displayed if JavaScript is disabled in the browser. The Interactive globe, sadly, will not because three.js needs JavaScript.
SSR/CSR Hybrid
SSR is a good option for data that changes relatively quickly, but when using SSR, the data will only be “fresh” when a user first visits the page. If data were to change after the page has loaded, users would need to refresh the page to see the latest updates. This is where a hybrid SSR with CSR (Client-side request) can help.
Latest Reaction
The Latest Reaction Feature uses this hybrid SSR/CSR approach. If Javascript is disabled in the browser, the Latest Reaction is rendered using SSR. However, if JavaScript is enabled, I poll the Fauna database using a Gatsby Serverless Function every 60 seconds and retrieve the latest reaction. This means the Latest Reaction is never more than 60 seconds out-of-date and will refresh without users needing to reload the page.
SSR/SSG Hybrid
Again, whilst SSR is a good option, is it always needed? Some of the data changes that occur on my site happen because of something I change. E.g I write a new post, commit the changes and trigger a build. In this instance, I’ve opted for a hybrid SSR with SSG (Static Site Generation) approach. The page is still Server-side rendered but the charts are statically generated. (Yes, a page can be both SSR and SSG.)
Data Charts
I’ve used this hybrid approach and created 4 data visualizations to help me better understand the frequency with which I write and the kind of content I’m writing about, and who for. Each of these charts is populated by data from around my site that has been extracted from the frontmatter in my .mdx
files and then queried from Gatsby’s data layer using GraphQL.
These charts allow me to plot or count the number of posts or articles I’ve posted each month over the last four years, the amount of posts or articles I’ve posted on each day of the week, the external publications I’ve written for (excluding Gatsby) and how many times, and then finally, a chart to show the total count for each tag used in all of the posts and articles.
As mentioned, the dashboard of my site is Server-side rendered, but all of the above can be considered “static,” and generally speaking, when folks refer to a static site, they’re probably talking about text and images on a page.
The SSG data for the charts can’t really go out of sync because each time I write a new post or article, I commit the .mdx
file to my repository, which in turn kicks off a new build in Gatsby Cloud.
This data can therefore be considered up to date as of the time the page loaded and likely won’t change during the duration of a page view.
The way I’ve created these charts uses the same technique, but because I like data, I chose to turn it into something more visually interesting. (fun fact, none of these charts were created using a charting library).
They are all hand-crafted using good ol’ mathematics, the SVG element, and with help from the following folks and their great tutorials — all will work with JavaScript disabled in the browser! 💅.
I prefer this hand-crafted approach as I found I have more control over the final output. When I’ve used charting libraries in the past I always seem to be hacking over the top of something to get the desired look, and not all charting libraries will work if JavaScript is disabled in the browser.
Deferred Static Generation (DSG)
With the release of Gatsby 4 (October 2011), Gatsby announced DSG. This page rendering method is similar to the tried and tested SSG approach (where pages are statically rendered on the server at build time), but the key difference is when.
Historically speaking, all Gatsby pages were SSG, and all pages would have to be built ahead of time. Whilst this often results in better SEO and a faster user experience than SSR, it can have adverse effects on build times.
Builds tend to fall into two main categories, and then there are a few subcategories for each:
- Local Development
- Content changes
- Code changes
- Changes that affect every page (e.g. Header/Footer)
- Changes that affect a single page
- Production Deployment.
- Content changes
- Code changes
- Changes that affect every page (e.g. Header/Footer)
- Changes that affect a single page
When developing a Gatsby site locally, it’s not always advisable to build every page, fortunately, while developing locally you probably won’t need to build all the pages.
You can use this “trick” if you like, it works a treat!: How to “fix” Gatsby’s slow local build times.
For production, however, Gatsby will need to build every page and if your content is changing quickly or you have multiple content creators working on your site, they’ll need to see the built page in a timely fashion.
Content creators typically won’t be using local .mdx
files as I have on my site to write content, so Gatsby/Gatsby Cloud has a number of super fast preview options for popular CMSs such as Contentful, WordPress, Sanity, and many more!.
Specifically, in the case of production sites and content changes, DSG can be configured to defer the static generation of any page or type of page.
In short, by using DSG, Gatsby hands control of Static Site Generation over to you, the developer. By choosing which pages to defer, you have more control over your build times. After all, you know your site better than anyone, so you’ll be able to create a custom defer strategy that works best for your needs.
How Does Gatsby’s DSG Work?
Let’s start with the output. A page created using DSG is the same as a page that has been created using SSG. Meaning: it’s a fully constructed HTML page that’s been pre-built and cached on the server ahead of time and is sent to the browser when a user visits that page.
It contains all the important metadata that Google needs to index your site and since the page is pre-built, it’s super fast and provides the best user experience for end users.
When this page is rendered, however, is where DSG comes in.
If a page is deferred using DSG, then Gatsby won’t pre-build it when you deploy your site. Instead, the first time a user visits that page Gatsby will build it on the fly, or just-in-time, and then send it to the browser when it’s ready. How is this different from SSR, then?
How Is DSG Different From SSR?
The above will only happen the first time a page is visited. When one user has visited a page once, the next user will be served an ahead-of-time pre-built SSG page from the cache, and every visitor after the first will experience the same speed as if the page were rendered using SSG.
With SSR, every visitor gets the same, sometimes slow experience, as they have to wait for the Server to generate the page before it’s sent to the browser. Typically, this leads development teams to optimize then cache headers, which is error-prone and oftentimes complex. This leads to pain for teams, and I prefer to minimize my own!
When To Defer Using DSG
I’ve seen some really interesting defer strategies from a number of our customers. Some choose to defer pages that aren’t visited that often and this strategy is determined using Google Analytics and page view statistics. Others defer based on the date a post or article has been published; some defer based on the popularity or stock level of a product. The options you have available to you, are quite frankly endless, each business has its own use case and Gatsby is flexible enough to accommodate any and all eventualities.
Here’s a diff
of the classic createPage
; with DSG, you could defer all but the latest 100 posts (provided the posts have been sorted by date first, of course!)
const posts = result.data.allMdx.nodes posts.forEach((post, index) => { createPage({ path: post.slug, component: path.join(__dirname, `./src/templates/posts.js`), context: { id: post.id, }, // index is zero-based index
+ defer: index + 1 > 100, })
})
…and for the curious, you can also enable DSG in our alternative page creation method: File System Route API.
export async function config() { return ({ params }) => { return { defer: // your defer strategy }; };
}
This one small change typically results in a quite drastic reduction in build times. Of course, it depends upon the use case, but some of our customers have reduced their build times by over 50%, which means that each and every build gives them time back in their day to do more interesting things than wait for a build!
Serverless Functions
In the summer of 2021 Gatsby released Functions, I’ve used Functions for each of my; post, article, demo, or stream pages to capture visitors’ reactions.
Using a set of SVG emojis, I invite users to leave a reaction to my content. When any of the emojis are clicked, I post to a Serverless Function from the client with the following payload.
await fetch('/api/add-reaction', { method: 'POST', body: JSON.stringify({ title: title, slug: slug, reaction: reaction, date: new Date() })
});
The Serverless Function, in turn, securely posts to a Fauna Database where the data is stored and ready to be retrieved and counted on the SSR page as mentioned above.
const faunadb = require('faunadb'); export default async function handler(req, res) { const { title, slug, reaction, date } = JSON.parse(req.body); const q = faunadb.query; const client = new faunadb.Client({ secret: process.env.FAUNA_KEY }); try { await client.query( q.Create(q.Collection(`reactions_${process.env.NODE_ENV}`), { data: { title: title, slug: slug, reaction: reaction, date: date } }) ); res.status(200).json({ message: 'Lovely stuff, your reaction has been added!' }); } catch (error) { res.status(500).json({ message: error.message }); }
}
Framework Improvements
We’ve made improvements to the core framework by adding a few new APIs too! The new Script API, available from 4.15.0, among other things, comes with an off-main-thread
script loading strategy which can be used to offload third-party scripts (such as Google Analytics) using Builder.io ’s Partytown 🎉.
Offloading third-party scripts to a Web Worker is a nice way to speed up page loads. I wrote a little more about this on my site: How to use Gatsby’s Script API with Google Analytics.
We also have the new Head API. Historically speaking, the recommended way to add metadata to your HTML page with Gatsby was to use react-helmet
and gatsby-plugin-react-helmet
.
Not anymore; from release 4.19.0, this functionality is included in the framework. I’ve written a post detailing a common migration pattern if you’re interested to know more. How to use Gatsby’s Head API with MDX.
Stay tuned for Slices API 🍕. There’s an open RFC here on the Gatsby GitHub: RFC: Slices API.
Gatsby Cloud
The Gatsby framework is free and open source and can be deployed on any number of hosting providers. We have created Gatsby Cloud as a convenient way to streamline your developer experience, with no additional plugins or configuration required. Put simply: it’s the best place to build, preview, and deploy your Gatsby site.
Whilst many in the past have complained about Gatsby’s slow build speeds, I often ask, have you tried Gatsby Cloud?
Here are some build speed benchmarks for my site, which is currently ~110 pages. These are all .mdx
with the exception of the dashboard, which, as mentioned, is Server-side rendered.
Many of these pages contain code block syntax highlighting, featured and embedded images, embedded Tweets, Code Sandboxes, and YouTube videos. All of this adds to build times, not to mention MDX taking a little longer to transform than good ol’ Markdown (.md
).
Note: These results are from the free tier of Gatsby Cloud (running in Pro mode for the 14-day trial).
From bottom to top, I’ll explain the results:
- Triggered by Gatsby Cloud:
03.22
This is the first time I deployed, and the site is built from a cold cache. - Triggered by a manual Build:
03.48
This is a manual build, triggered by me, and is built from a cold cache. - test: content change 1:
55s
This build was triggered by a commit to GitHub after making a change to one of my posts and is built from a warm cache.
Cold cache builds usually take longer as Gatsby Cloud has nothing to intelligently compare “what’s changed.” However, when there is a cache and the brains behind Gatsby Cloud do their thing, the build speeds are great! Personally, as a hobbyist developer, I’m fine with waiting 55 seconds for my site to be built and deployed.
(Re) Introducing Gatsby, A Reactive Site Generator
If the above isn’t enough to prove Gatsby is a lot more than simply a Static Site Generator, have a read of Gatsby’s CTO’s recent post that describes how Gatsby takes advantage of a reactive approach to regenerating a “static site” when content changes occur: (Re-) Introducing Gatsby, A Reactive Site Generator.
TLDR; When a Gatsby 4 site is deployed to Netlify or Vercel, it can only behave as if it were an SSG. When deployed to Gatsby Cloud, it can behave as though it were an RSG, decreasing build speeds in static page re-generation by 100x!
In this video, Gatsby’s CTO Kyle Mathews demonstrates Gatsby Cloud reactively generating and then deploying a static page in ~2 seconds and all from the push of a single button, our CMS previews have been able to do something similar for a while but never before has it been this fast!
Gatsby has evolved quite dramatically in the last two years. If your experience is with Gatsby 2, you may be surprised at how much faster and more flexible it has become with new page rendering modes and capabilities like DSG and SSR, and we’ve got way more updates to come!
I think you’ll like what the hard-working folks over here have been doing, and if you do have any questions, please come say hello on Twitter. 😊
Further Reading
(vf, il)