I've been building my website with the help of an amazing CSS-in-JSS solution called Stitches. If you haven't heard of it before I would definitely recommend taking a peek at their website and giving it a try because I almost instantly converted across from styled-components
.
This isn't a post all about how great I think Stitches is, as much as the dev team would probably love it, though. Instead, I wanted to quickly write-up how I got dark mode working on this site using Next.js, Stitches and a little package called next-themes
.
Install next-themes
with:
npm install next-themes
and add its provider to our Next.js App
component.
// _app.tsx
import React from "react";
import type { AppProps } from "next/app";
import { ThemeProvider } from "next-themes";
function App({ Component, pageProps }: AppProps): JSX.Element {
return (
<ThemeProvider>
<Component {...pageProps} />
</ThemeProvider>
);
}
export default App;
Head on over to your Stitches config file (stitches.config.ts
, for me) and import createTheme
from stitches/react
. Then create a new theme for your dark theme like so:
// stitches.config.ts
import { createStitches, createTheme } from "@stitches/react";
export const { styled, css, theme } = createStitches({
// other styling here...
});
// Create your new dark theme below, like this.
export const darkTheme = createTheme("dark", {
colors: {
text: "black",
heading: "grey",
background: "white",
accent: "cyan",
},
});
Great! Now we want to set this theme up as an option in next-themes
. Head back over to the _app
file and set it up like so.
// _app.tsx
import React from "react";
import type { AppProps } from "next/app";
import { ThemeProvider } from "next-themes";
// Import your dark theme into the component.
import { darkTheme } from "../stitches.config";
function App({ Component, pageProps }: AppProps): JSX.Element {
return (
<ThemeProvider
attribute="class"
value={{
dark: darkTheme.toString(), // this .toString() is important
light: "light",
}}>
<Component {...pageProps} />
</ThemeProvider>
);
}
export default App;
Lastly we just need to make a little button or something to let you toggle between the two colour themes.
// ThemeToggle.tsx
import React, { useState, useEffect } from "react";
import { useTheme } from "next-themes";
export const ThemeToggle = () => {
const [isMounted, setIsMounted] = useState(false);
const { setTheme, resolvedTheme } = useTheme();
useEffect(() => setIsMounted(true), []);
const toggleTheme = () => {
const targetTheme = resolvedTheme === "light" ? "dark" : "light";
setTheme(targetTheme);
};
// Don't render the theme-toggling UI before the mounted.
// See: https://www.npmjs.com/package/next-themes#avoid-hydration-mismatch
if (!isMounted) {
return <button>Loading...</button>;
}
return (
<button onClick={toggleTheme}>Switch to {resolvedTheme === "light" ? "dark" : "light"}</button>
);
};
Add the button wherever you want it and you should see your theme switch between dark and light mode. It'll also remember which mode you left selected and use it on subsequent page refreshes!