The Great Gatsby Filesystem
Gatsby; that Other Static Site Generator
I make a bunch of websites. Like, a lot of websites. When you’re that, it’s good to rely on a standard set of tools, and more and more I’m leaning on Gatsby for the heavy lifting. It’s got a core group of features that are supplemented by an even richer plugin library.
For most of my projects I’ve needed some more hands-on customization and I don’t see a whole lot of Gatsby blogs talking about exactly how things are set up. So it’s up to this Hugo blog to do it!
So that’s where I’ll step up; I’m revealing the deep dark secrets of my
gatsby-config
file and showing some of the upgrades and customizations I’ve
made to make my life easier when using Gatsby.
In the future, I may make a fully annotated Gatsby repo with comments… but honestly that’s a lot of work to maintain. If you really want that, send me an email; yocky@yockyard.com. Maybe I’ll set that up… but for now, on to the customizations!
Dev vs. Prod Favicons
I often run pre-production builds on my sites, and that usually means that I want to know when I’m looking at pre-production while leaving the majority of the site the same as production. This situation comes up a lot: when I worked alongside a SalesForce admin, they’d actually install a Chrome plugin to show different bookmark icons depending on if they were in DEV, QA, or Produciton. It’s a really bad feeling to know you’ve just sent an expensive or dangerous command to Production!
My gut reaction to this situation was, why not actually make the favicons different for the different environments?
In Gatsby most people are probably going to use the
gatsby-plugin-manifest
to handle the favicon. The default configuration wants you to provide a string to
the path for the favicon, but for my configuration I need to key off of the
build environment. That means I need something like the NODE_ENV
environment
variable. I also tend use the
cross-env package to standardize
environment variables. Finally when I run my build:QA
and build:PROD
jobs I
automatically set a new environment variable using cross-env
. Those jobs look
like this:
{
"start": "cross-env BUILD_FLAG=DEV gatsby develop",
"build": "gatsby build",
"build:QA": "cross-env BUILD_FLAG=QA gatsby build",
"build:PROD": "cross-env BUILD_FLAG=production gatsby build",
}
With that all set up, I can now rely on the build environment and dynamically assign the path to the icon before we start configuring the rest of the plugins:
const BUILD_FLAG =
process.env.BUILD_FLAG ?? process.env.NODE_ENV ?? "production";
let icon = "src/images/env/dev/favicon.png";
if (BUILD_FLAG === "QA") {
icon = "src/images/env/qa/favicon.png";
} else if (BUILD_FLAG === "production") {
icon = "src/images/env/prod/favicon.png";
}
// ...
{
resolve: "gatsby-plugin-manifest",
options: {
icon,
name: `your-site`,
short_name: `site`,
start_url: "/",
background_color: `#FFFFFF`,
theme_color: `#FF0000`,
cache_busting_mode: "none",
display: "standalone",
include_favicon: false,
icon_options: {
purpose: "maskable",
}
},
},
// ... the rest of your config file
If you’re able to design a favicon with a single distinguishing color, then just rotate that hue either 180°, or 60° if you need 3 (dev, prod and pre-prod). If you don’t have a single color for your favicon (and you’re not Google), then maybe consider doing that.
Customizing Files
Running a pre-prod version of the site usually means you have more pages on the non-prod versions of your site. If you want your professional façade to persist, you probably want to hide those “test” pages that are so very useful but also not the most refined.
To manage this I use an entirely new file, because this can get messy and
unwieldy quickly. At the top of my gatsby-config.js,
I’ll source a
./files-config
, at the top of the file. Within that, I’ll lay out the file
systems to include and I can split up files into arrays: “alwaysInlcude,”
“nonProd,” “devOnly,” “qaOnly,” and “prodOnly.” With those arrays, export a
configureFileSysyem
that looks something like this:
export default function configureFilesystem({env="production"}) {
switch(env.toLowerCase()) {
case "dev":
return alwaysInclude.concat(nonProd, devOnly);
case "qa":
return alwaysInclude.concat(nonProd, qaOnly);
case "production":
default:
return alwaysInclude.concat(prodOnly);
}
}
For some of my projects I have some “missing-asset” assets, which I don’t want going to production. These are usually stock pictures, memes, or Lorem Ipsums which I want to use with my various test pages to see how things will look and work before introducing real content. With my set up above not only do you avoid using “stock” photos from pixabay in embarrassing locations, if you actually do try to push a production build with non-prod assets it’ll fail when the page queries try to source the missing assets meaning you’re protected from near accidental inclusions of cat pictures. If that’s not a good fail-safe for you, then you’re much braver than me.
TypeScript Everywhere
TypeScript is the best thing to happen to JavaScript before ES6. If you’re not using Typescript in your JavaScript projects then you’re really missing out. Fortunately, Gatsby comes out of the box with TypeScript support, and setting up interoperability isn’t too bad.
I go ahead and use typescript even on the gatsby-config
file, so I can get
that sweet typing hint system. Doing this is pretty straightforward; in
gatsby-config.js
, move most of your actual configuration into
gatsby-config.ts
, and in the original file use the following line:
require('ts-node').register({ files: true, transpileOnly: true});
and there you go! Set up your pages as .tsx
instead of .jsx
and the rest of
the TypeScript system is yours to use.
Now coming from the React world you may not want all this “typing” and “restriction,” because of that free-form fast-and-loose feel you get from the “library not a framework” world that React gives you. I’m sure that’s great if you’re a highly consistent and organized person, but for the rest of us mortals, having an idiot-proof system that tells you why your component can’t render an array of sub-pixel information arrays is helpful. Put another way, TypeScript is a rails system I hate writing in the moment, but the day after I’ve shipped that feature those rails and blueprints become the documentation I’m also not the best at writing.
GraphQL and its Magic
If you really want to use the magic of TypeScript in Gatsby you also want to utilize the GraphQL code generation plugin. This package handles the gap between the Magic of GraphQL queries, which are absolutely necessary for managing your site’s content. Ok, maybe not absolutely necessary, but man is it convenient not to have to type and retype your query data after the machine has already inferred what’s supposed to happen. Not using this would be like putting away a mystery book before the final chapter: all the work has already been done, you just have to read the last bit to know the truth!
The magic for this plugin comes when you name a GraphQL query, and you can do that whenever you make any page template, or when you make a one-off page… so like all the time. Once you’ve named your query, you’re able to import a generated type file with the types that GraphQL automatically generates.
It’s honestly amazing when it works.
All you need is to include this plugin in your gatsby-config.ts
file:
plugins: [
"gatsby-plugin-graphql-codegen",
]
Then in any of your files that have a named GrapqhQL query you can import
directly from graphql-types
.
import { MdxPageQuery } from "graphql-types";
And you’re in the clear to start using the queries you write for those pages
with strong typing. This is helpful for quelling those messages about not
having the correct typing on the data types that come out of the GrapqhQL
queries you’re writing for those pages, or for shutting up the fact that your
data
class has an implicit any
tempting you to tear out the ts
compile
step entirely.
The Rest
9 times out of 10 you’re running the same stack as before, and that’s not a bad thing. I see myself adding these to 99% of the projects I want to run:
gatsby-plugin-mdx
because why not organize your content with markdowngatsby-plugin-root-import
for convenience in my source.tsx
filesgatsby-transformer-json
for my semi-structured datagatsby-plugin-advanced-sitemap
for a good robotic experiencegatsby-plugin-react-helmet
for handling the intricate header-level details for your pagesgatsby-plugin-typography
for good measure.
For styling, and CSS classes I have become a big fan of styled components, so
to make that work with Gatsby I include gatsby-plugin-styled-components
. With
that I’m basically set up to start any new project I’d like. I’ll probably do an
article on styled-components
and what they bring to the table, but for now
this article is already getting too long.
The Green Light for Static Site Generation
Gatsby is a great tool (even if it is a bit slower than Hugo), as it provides a modern library for making great content. It’s wide open for customization and expansion.
Gatsby isn’t perfect; I’ve actually skipped over a rough patch or two that took me a few hours to solve especially when it comes to the GraphQL typing system. In spite of the more nasty technical difficulties I’ve run into while working with Gatsby, it’s been an amazing tool to work with. I hope this article clues you in to modern Gatsby development, and fills a few gaps in what’s possible with this tool.
If you’ve got any questions, make sure to drop me an email at yocky@yockyard.com and mention the “Gatsby Filesystem” post, I’m probably going to expand on this series a bit more.