Sabbatical dev

sabbatical devtechnical blog
 

Stop the Dark Mode Flash in MUI 5 with Next.js

Using cookies to render the correct theme

After setting up dark mode on Material UI version 5 (MUI 5) with Next.js, and being happy that everything seemed to work fine, I pushed a production build to Vercel.

Upon setting dark mode on the published app, and then reloading the page (which is rendered from the server), I noticed the light theme for a split second before the switch to dark.

This created quite a jarring flash, and I really wasn’t happy with it.

The few posts I found that dealt with this issue did not really deal with MUI. Many suggested using global CSS variables with Emotion, but this seemed overly complicated and not really doable.

I decided to look more deeply into my set up, (which was built using guidance from this post).

As detailed in the post, when you set up MUI 5 with Next.js, you create a _document.js file that uses the Emotion cache. This prints out the essential CSS needed to the rendered file, so the styles are instantly available and not loaded after.

I wondered while looking at my _app.js file, if setting the default theme to dark would instruct whatever builds this CSS string under the hood from Emotion, to use the dark theme styles on render.

const [mode, setMode] = React.useState<PaletteMode>("dark");

This worked, so whichever default theme was set, the theme styles would be selected and printed to the file.

I now just needed a way to check which theme had been selected by the user, before rendering the page.

Cookies

You cannot read from local storage server-side but you can read a cookie. I had to change my implementation and stored the value of the users theme selection in a cookie. I used react-cookie and cookie.

Once the cookie exists you retrieve the value by adding getInitialProps to the App

App.getInitialProps = async ({ ctx }: AppContext) => {
  let themeSetting;
  if (ctx.req && ctx.req.headers.cookie) {
    themeSetting = parseCookies(ctx).cookieColorMode;
  }
  return {
    themeSetting,
  };
};

and passing the cookie value along with any other initial props.

We pass this value as the default theme to the hook.

const [mode, setMode] = React.useState<PaletteMode>(
    props.themeSetting || 'light'
 );

And the page is now rendered with the correct CSS from the user's chosen theme, and no flash.

disclaimer: In my implementation I also check for whether the user has dark mode set as a preference from their system. If so, there will be a one time only flash. I cannot find a way of getting this information server-side, as the info is only retrieved from a media query. I do however set the cookie to this selection after, and personally I can live with this one time only flash.

Here you can see the simple dark mode application.

And here is the repo with the implementation


Comments

Add a comment