Add a dark mode toggle to your Tailwind & Gatsby site
Getting this set up perfectly (no FOUC, persistence) was a little bit tedious, so I thought I'd christen the blog with a little tutorial.
Gatsby setup
I'm using use-dark-mode
for this which is a pretty simple React hook that handles persisting the user's choice and adding the relevant class to the body.
npm install use-dark-mode
You'll then also want to use gatsby-plugin-use-dark-mode
, which injects a script in to your page that makes sure the dark mode class is appended to the body before your page renders. This is what stops you having a flash when the page initially loads.
npm install gatsby-plugin-use-dark-mode
Next, you'll add the plugin in to gatsby-config.js
{
resolve: 'gatsby-plugin-use-dark-mode',
options: {
classNameDark: 'dark',
},
}
Note that we're overriding the default class name here - by default use-dark-mode
will add a dark-mode
class, but this doesn't match up with what Tailwind expects.
The Tailwind side
Tailwind makes this pretty easy, all you need to do is modify your config to add the below
{
...,
darkMode: 'class'
}
This tells Tailwind you want to toggle dark mode manually with a class, rather than just applying the user's system default.
If you want to change the body's background like I do on this site, you'll want to add this to your global CSS (wherever you add the @tailwind
stuff), of course changing the colours to whatever you want.
body {
@apply bg-zinc-100;
}
body.dark {
@apply bg-zinc-800;
}
Applying dark mode classes works as normal in tailwind, you just prefix your class with dark:
, for example
<span className="text-zinc-800 dark:text-zinc-100">Text</span>
Putting it together in React
I have a DarkModeToggle
component that (funnily enough) toggles dark mode!
The important bit is this line
const { toggle } = useDarkMode(undefined, { classNameDark: 'dark' });
which tells use-dark-mode
to use the system default, and apply the dark
class to the body when the mode is changed. This has to match up with the class you gave to gatsby-plugin-use-dark-mode
above.
You can then call toggle
to change the mode whenever you want. This'll be persisted in local storage and you won't have any flashing on first render.