Sat Aug 28 2021

Reducing Gatsby HTML file size by removing inline styles

The static/dynamic site generator Gatsby is a great tool for generating semi-static websites. However, if you are using CSS modules and CSS frameworks like bootstrap (or just have a lot of CSS), then individual HTML files can become quite large.

Why does this happen

When Gatsby builds a page it tries to make sure that every pages is as "standalone" as possible. Meaning that the user could request a page at any path and still get a fast response. To achieve this Gatsby will bundle all of your CSS needed for that page into an inline style tag. The idea is that this will make it unnecessary for the browser to load an external stylesheet before drawing the page. That's actually a good idea, but a bug/oversight in the Gatsby bundler will (at the time of writing this) cause all of the CSS to be bundled into every page. This is a known issue and the people working on Gatsby will probably fix it, but in the meantime, here is how you can fix this today.

How to remedy this problem

The fix do involve removing inline styles from each page and relying on an external stylesheet instead. In modern browsers supporting http2 loading this extra file shouldn't affect loading performance much.

Here's how to remove the inline style from each HTML page.

In gatsby-ssr.[js|jsx|tsx] add the following (note that this is Typescript):

import { PreRenderHTMLArgs } from "gatsby";

export const onPreRenderHTML = ({ getHeadComponents }: PreRenderHTMLArgs) => {
	if (process.env.NODE_ENV !== "production") return;

	getHeadComponents().forEach((el: any) => {
		if (el.type === "style" && el.props["data-href"]) {
			el.type = "link";
			el.props.href = el.props["data-href"];
			el.props.rel = "stylesheet";
			el.props.type = "text/css";

			delete el.props["data-href"];
			delete el.props.dangerouslySetInnerHTML;

This piece of code will replace all inline styles with a link to the stylesheet instead. Note that it will only run when using gatsby build, if you want it to run for development just remove the first line of the `onPreRenderHTML` function.