
Replace Inline Styles in Gatsby with an External CSS File

Unless you chose to use styled‑components or css‑modules (which you really should!), Gatsby will default to building a (potentially massive) inline style block with the entire contents of your project's CSS right in it at the head of your page.
This should not be seen as an issue: because of the way that the internal navigation with Gatsby (and React) works, the majority of the page markup is only ever loaded once per visit. Inlining makes a noticeable difference to the perceived speed that a page loads at: the browser has less to download before the page starts to render (with styles already in place), whereas an external CSS file gets downloaded separately, and generally after the page markup.
Nevertheless, there are any number of different reasons why this default behaviour might not be to your preference, and it is something that is regularly posted about on both GitHub and Stack Overflow. The good news is that it's not a difficult thing to do; I've toggled back and forth between inline and external stylesheets on my own personal website, and prefer a gently‑modified version of Glinkis's solution on GitHub.
When building, Gatsby passes everything that appears within head ‑ including the inlined CSS ‑ into html.js as a headComponents prop. This includes the source of the compiled CSS as a data‑href attribute. You will also see this attribute if you view source on your production build.
So all we need to do is modify this prop before it is output into the template:
if (process.env.NODE_ENV === 'production') { props.headComponents.map(component => { const { type, props: thisProps } = component; if (type === 'style') { const link = ( <link rel="preload" as="style" href={thisProps['data-href']} /> ); const index = props.headComponents.indexOf(component); props.headComponents.splice(index, 1, link); } });}What This Does:
- Checks to make sure that this is a production build;
- Loops over each of the
headComponentsprops ascomponent; - If the
typeis 'style', then we build a new link element using thedata‑hrefattribute to populate thehref. - We then work out the original index of this component within the
headComponentsprop (this is important: it will not necessarily be the current index of the map ‑ there are other types in there too); - And slot it back into
headComponents.
And that is it!
Considerations Around Performance
One thing to note is that rather than using the traditional rel="stylesheet" format, I'm using a combination of preload and as. If your hosting is configured for HTTP/2 then this will be interpreted as a server push and will allow the browser to fetch it earlier. If you don't use HTTP/2 then it will degrade gracefully, or you could also simply switch that const out with:
const link = <link rel="stylesheet" href={thisProps["data-href"]} />Fin.
Related Articles

Why I’m the Best Choice for Web Development Near You. 
How Inheritance Works in the JavaScript Prototype Chain. How Inheritance Works in the JavaScript Prototype Chain

Converting Between Camel, Snake, and Kebab Case in JavaScript. Converting Between Camel, Snake, and Kebab Case in JavaScript

Validating Parentheses Input Using TypeScript. Validating Parentheses Input Using TypeScript

Resolving mini‑css‑extract‑plugin Warnings in Gatsby. Resolving
mini‑css‑extract‑pluginWarnings in Gatsby
Adding Static Files to a Gatsby Site. Adding Static Files to a Gatsby Site

CSS box‑sizing: Controlling the Element Box Model. CSS
box‑sizing: Controlling the Element Box Model
LeetCode: Finding the Diameter of a Binary Tree. LeetCode: Finding the Diameter of a Binary Tree

Using JavaScript to Avoid Orphans. Using JavaScript to Avoid Orphans

Declarative vs. Imperative Programming. Declarative vs. Imperative Programming

Understanding the Composition API in Vue 3. Understanding the Composition API in Vue 3

Building Custom Directives in Angular. Building Custom Directives in Angular