The Great Gatsby Filesystem

Tags: Gatsby, Webpage

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 markdown
  • gatsby-plugin-root-import for convenience in my source .tsx files
  • gatsby-transformer-json for my semi-structured data
  • gatsby-plugin-advanced-sitemap for a good robotic experience
  • gatsby-plugin-react-helmet for handling the intricate header-level details for your pages
  • gatsby-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.