Gatsby JS — How to solve FOUC when using tss-react and Material UI v5
Tags: fouc, tssreact, materialui, gatsby
Published: 2022-01-29 17:01:36


[Material UI](https://mui.com) v5 brought some amazing updates, but switching from [JSS](https://cssinjs.org) to [Emotion](https://emotion.sh/docs/introduction) had an arguably nasty side-effect: it was no longer as straightforward to group your component styles in classes. Fortunately, a fantastic library emerged that allowed developers to not only reduce the extreme pain from migrating all their classes from v4's `makeStyles` to emotion, but to also to continue to writing classes in practically the same syntax, with wonderful TS type-safety. This library was [tss-react](https://github.com/garronej/tss-react/), and it was one of my favorite open source discoveries of 2021.

Unfortunately, however, `tss-react` and `material-ui` don't exactly work "out of the box" when it comes to [Gatsby JS](https://www.gatsbyjs.com). Gatsby is a fantastic framework for generating static websites with React code. Static websites are great because they load fast, can be largely cached in a visitor's browser and have great Search Engine Optimization (SEO).

The trouble I was having was that my production website was displaying what is known as a ["flash of unseen content"](https://en.wikipedia.org/wiki/Flash_of_unstyled_content), or FOUC. Material UI users have known about this problem for a long time — in fact, there even exists a [popular plugin](https://github.com/hupe1980/gatsby-plugin-material-ui/) for solving this exact issue.

But because `tss-react` and `material-ui` do not share the same `emotion` cache, then simply adding `gatsby-plugin-material-ui` doesn't work. A more custom solution is needed.

**This post is about how I solved the FOUC-problem in my Gatsby JS website after migrating to Material UI v5 with `tss-react`.**

---

## TLDR:
- copy paste the two files below
- make sure your `<CacheProvider />` component imports your emotion cache from the new place
- remove `gatsby-plugin-material-ui` from your `gatsby-node.js` file if you had it

## Showcase:

> **Here's a [sample implementation](https://github.com/Deckstar/gatsby-tss-example).** Feel free to copy from it! 😉

This little example project should be helpful if you get stuck 🙂

---

## Explained

### Step 1: make an emotion `cache` that can be used both on the client and the server

This is the cache that will be used by emotion to make your Material UI styles.

Unfortunately, you probably want this file to be a vanilla JS file, without TypeScript. This is because the `gatsby-srr.js` file that we will have to create later must be a vanilla JS file, and cannot parse uncompiled TypeScript. But don't worry — you can still get some TS typing intellisense just by adding a few JSDoc comments! ;)

```JavaScript
// ./src/theme/cache.js

import createCache from '@emotion/cache';

/** @type {import('@emotion/cache').Options} */
export const cacheProps = {
	key: 'mui',
	prepend: true,
};

/** @type {import("@emotion/cache").EmotionCache | undefined} */
export let muiCache;

export const makeMuiCache = () => {
	if (!muiCache) {
	muiCache = createCache(cacheProps);
	}

	return muiCache;
};
```

Don't forget to use this cache for your `<CacheProvider />` that wraps your Material UI `<ThemeProvider />`:

```TSX
// example

import { makeMuiCache } from './cache';

const muiCache = makeMuiCache();

const ThemeWrapper = (props: { children: ReactNode }) => {
  const { children } = props;

  return (
    <CacheProvider
		value={muiCache} // <-- use the new cache here
	>
      <ThemeProvider theme={yourTheme}>
        {children}
      </ThemeProvider>
    </CacheProvider>
  );
};

```

### Step 2: create a `gatsby-srr.js` file, and add the generated styles to the server-side `<head>`-components

You'll probably want it to look something like this:

```jsx
// ./gatsby-srr.js

import { CacheProvider } from '@emotion/react';
import createEmotionServer from '@emotion/server/create-instance';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { getTssDefaultEmotionCache } from 'tss-react';

import { makeMuiCache } from './src/theme/cache';

/** @param {import('gatsby').ReplaceRendererArgs} args */
export const replaceRenderer = (args) => {
  const { bodyComponent, replaceBodyHTMLString, setHeadComponents } = args;

  const muiCache = makeMuiCache();
  const { extractCriticalToChunks } = createEmotionServer(muiCache);

  const emotionStyles = extractCriticalToChunks(
    renderToString(
      <CacheProvider value={muiCache}>{bodyComponent}</CacheProvider>
    )
  );

  const muiStyleTags = emotionStyles.styles.map((style) => {
    const { css, key, ids } = style || {};
    return (
      <style
        key={key}
        data-emotion={`${key} ${ids.join(` `)}`}
        dangerouslySetInnerHTML={{ __html: css }}
      />
    );
  });

  const tssCache = getTssDefaultEmotionCache();
  const { extractCritical } = createEmotionServer(tssCache);
  const { css, ids } = extractCritical(renderToString(bodyComponent));

  const tssStyleTag = (
    <style
      key="tss-styles"
      data-emotion={`css ${ids.join(' ')}`}
      dangerouslySetInnerHTML={{ __html: css }}
    />
  );

  const combinedStyleTags = [...muiStyleTags, tssStyleTag];
  setHeadComponents(combinedStyleTags);

  // render the result from `extractCritical`
  replaceBodyHTMLString(emotionStyles.html);
};
```

The idea here is that we are exporting a `replaceRenderer` function that Gatsby will use internally to render your page when generating it (see Gatsby the docs [here](https://www.gatsbyjs.com/docs/reference/config-files/gatsby-ssr/#replaceRenderer)).

The first part of this function is [copy-pasted straight out of `gatsby-plugin-material-ui` ](https://github.com/hupe1980/gatsby-plugin-material-ui/blob/master/gatsby-plugin-material-ui/src/gatsby-ssr.js). However, where the plugin has to use clever file-system tricks to get access to your emotion cache, we can just import our own cache directly! With our cache available from our `makeMuiCache` function, we can easily generate our `muiStyleTags` using emotions server-side helpers.

`tss-react` is also very helpful — you can easily access its emotion cache using the `getTssDefaultEmotionCache` helper. As [Nicholas Tsim documents in his blog post](https://ntsim.uk/posts/how-to-add-vanilla-emotion-ssr-to-gatsby), it is then very easy to create the style tags for any emotion cache — much like how we did earlier for Material UI, we create another emotion server, extract the critical CSS and map it convert it into a JSX style tag.

We then combine all our style tags into one array, and use the Gatsby `setHeadComponents` helper so that all of them would be included on our page. And finally, we call `replacebodyhtml` with the generated HTML-string.

### Step 3: Remove `gatsby-plugin-material-ui` from `gatsby-config.js` to avoid conflicts.

Self-explanatory. If you were using the plugin before, then open your Gatsby config and delete it:

```JavaScript
// ./gatsby-config.js

module.exports = {
 /* Your site config here */
 plugins: [
   `gatsby-plugin-material-ui`, // <-- delete me!
	// ...rest plugins
 ],
 // ...rest config
};

```

Don't forget to run `yarn remove gatsby-plugin-material-ui` (or the same command with whatever package manager you're using), since the package is no longer needed.

---

That's it! Your Gatsby site should now load without any flash of unseen content, and you can continue to use the spectacular Material UI library in all its v5 glory, together with `tss-react`, of course ;)

Good luck!