Not so quick with the Gatsby site optimizations
This is the post where I talk about neglecting the site and pledge to blog more… wait, wasn’t I suppose to do this on January 1st?
Jokes aside, the last year and half I haven’t felt like writing. Between my “real” job, open source projects, COVID-19, buying and selling a house during a global pandemic, and moving the twins to a new school — where does someone find the time?
With life stabilized, I had time to dust the cobwebs off this site.
Build process
In the time that I ported over from Jekyll to Gatsby, GitHub released Actions to build, test, and deploy code. Migrating my build process over to GitHub Actions seemed obvious as I was hitting the limits of Travis CI’s free tier.
GitHub Actions migration
The process of updating involved creating a new YAML config file and adding environment variables. The cherry on top was leveraging an action to cache Gatsby’s .cache
and public
folders between builds.
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Caching Gatsby
id: gatsby-cache-build
uses: actions/cache@v2
with:
path: |
public
.cache
key: ${{ runner.os }}-gatsby-build-${{ github.run_id }}
restore-keys: |
${{ runner.os }}-gatsby-build-
Building the site from a cold start and no cache takes about 35 minutes to chug through 1,426 pages and resize/optimize 13,194 images. Each cached builds after takes ~1 minute to finish — that’s what I call an improvement!
Upgrade Gatsby to 3.x
Gatsby v3 added all sorts of improvements to the local development experience, which naturally I wanted to take part in. Unfortunately I had to wait for a few community plugins to be updated as the old Gatsby v2 versions broke my builds.
Plugin: gatsby-remark-source-name
Coming from Jekyll I’m used to organizing Markdown files into different collections like posts
, pages
, and comments
. With Gatsby I place each in their own folder with a matching name
in gatsby-config.js
as shown below:
// excerpt from gatsby-config.js
plugins: [
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'posts',
path: `${__dirname}/src/posts`,
},
},
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'pages',
path: `${__dirname}/src/pages`,
},
},
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'comments',
path: `${__dirname}/src/comments`,
},
},
]
The gatsby-remark-source-name plugin then adds these as a sourceName
field to Remark nodes. Allowing for an easy way to filter on collection type in my GraphQL queries:
# query for all "posts" Markdown files
return graphql(`
{
posts: allMarkdownRemark(
filter: {
fields: { sourceName: { eq: "posts" } }
}
) {
edges {
node {
html
frontmatter {
# Assumes you're using title in your frontmatter
title
}
}
}
}
}
`)
This broke with Gatsby v3 and threw the following error:
TypeError: Cannot destructure property 'createNodeField' of 'boundActionCreators' as it is undefined.
Turns out a plugin isn’t needed and you can achieve the same result by adding the following to gatsby-node.js
.
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === 'MarkdownRemark') {
const fileNode = getNode(node.parent)
createNodeField({
node,
name: 'sourceName',
value: fileNode.sourceInstanceName,
})
}
}
Plugin: gatsby-source-github-api
Not sure why gatsby-source-github-api had problems with Gatsby 3.x, but this was the error message I encountered:
ERROR #11321 PLUGIN
"gatsby-source-github-api" threw an error while running the sourceNodes lifecycle:
Cannot destructure property 'createNode' of 'boundActionCreators' as it is undefined.
8 | ) => {
9 | console.log(boundActionCreators)
> 10 | const { createNode } = boundActionCreators;
| ^
11 | return new Promise((resolve, reject) => {
12 | // we need a token to use this plugin
13 | if (token === undefined) {
File: node_modules\gatsby-source-github-api\gatsby-node.js:10:11
A quick update of the npm package and all was well with the world.
Migrating Gatsby from v2 to v3
For everything that wasn’t related to outdated community plugins, I followed the official migration guide. A couple of find/replace commands across my project and I was done.
The other big change came from migrating gatsby-image to gatsby-plugin-image. This involved updating dependencies, fixing GraphQL queries, and replacing components.
Running npx gatsby-codemods gatsby-plugin-image
did a lot of this work for me. Gatsby’s CLI tool is pretty good at letting you know if you’re using the deprecated gatsby-image component. It even let me know about instances where I should add alt
attributes, which is good for accessibility.
Passing Core Web Vitals
Only took me a year of tinkering to get 100’s in Google Lighthouse for performance, accessibility, best practices, and SEO. Shipping less JavaScript by installing gatsby-plugin-preact certainly helped improve performance scores too.
But on some of my heavier pages with tons of DOM elements, 3rd party embeds from Twitter, and large animated GIFs — perf scores still take a hit.
Gatsby supports both WebP and AVIF image formats now, so I’ll need to experiment with those to see if I can shave even more time off. Just as long as my build times don’t increase too much…
Accessibility improvements
- Adjusted the
:focus
and:hover
styling of grid and list entries to render an outline around each when selected. - Bumped up the padding around footer links so they’re easier to tap.
Dark mode updates
Flipping between light and dark themes on the site worked before, but it had room to improve. For starters it wasn’t checking if a user had color theme preference (light or dark) set in their operating system.
To remedy this I installed gatsby-plugin-use-dark-mode to add a custom React Hook for use in my “dark mode toggle” component.
As an added benefit it also injects a small bit of JavaScript that helps with the dreaded flash of default theme plaguing sites with dark modes.
Table of contents updates
I’m still using a feature of Gatsby’s Markdown transformer to automatically create table of contents from headings. But instead of dropping it into a collapsible <details>
element I styled the unordered list as a true sidebar.
Could just be me, but putting it off to the side has a nicer flow than before.
Visual refinements
And because I can’t leave anything alone I started tinkering with the design of a few things.
Home page
Replaced the glitched image of me on the home page with a faux Polaroid frame and fixed it’s alignment with the rest of the page.
Archive pages
Changed the styling of the related tags component from a multi-column to an inline list to save space. I also reused this component on archive pages to help surface related topics that might have been buried before.
Added the featured posts component (used on the home page) to all archive pages… if applicable.
Works page
Pulled in more GitHub repositories under the Open source contributions section, added fork counts, and switched to a staggered card layout to add interest.
Speaking of open source, I switched this repository to private on GitHub. Way too many lazy developers were reusing my content with minimal changes. I have nothing against people forking my work and making it their own. But replacing my name and social media links with your own and calling it a day is weak.
I may make it public again if I have time to submodule out the content, but that might be too much pain for what it’s worth.
Experimenting with ads
To help offset the small costs of keeping this site up I’m experimenting with Google AdSense again. I was ready to ditch them once I saw how bad they degraded page speed performance.
After messing around with an intersection observer, I was able to keep things in check by waiting to load the Adsense scripts. I’m sure this JavaScript I added to Gatsby’s html.js
file could be improved on. But for now it’s doing the trick and I’m pennies richer for it.
<script
dangerouslySetInnerHTML={{
__html: `
(function(window, document) {
function loadAds() {
// Load Google AdSense
var ad = document.createElement('script');
ad.type = 'text/javascript';
ad.async = true;
ad.dataset.adClient = 'ca-pub-xxxxxxxxxxxxxxxx';
ad.src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
var sc = document.getElementsByTagName('script')[0];
sc.parentNode.insertBefore(ad, sc);
}
var lazyLoad = false;
function onLazyLoad() {
if (lazyLoad === false) {
lazyLoad = true;
loadAds();
console.log("advertisements loaded");
}
}
if(!!window.IntersectionObserver){
let observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if(entry.isIntersecting){
onLazyLoad();
observer.unobserve(entry.target);
}
});
}, { rootMargin: "0px 0px 500px 0px" });
document.querySelectorAll('.adsbygoogle').forEach(ad => { observer.observe(ad) });
}
})(window, document);
`,
}}
/>
1 comment
Would you ever make a boilerplate Gatsby version/theme of this site available on GitHub?