NOTE: This tutorial is relevant to developers using a CSS-in-JS styling approach, e.g. Emotion.js, Material UI, etc.
Step 1: Use Recoil atom for the currently selected theme's name
import { PaletteMode } from "@mui/material";
import { atom } from "recoil";
export const ThemeName = atom<PaletteMode>({
key: "ThemeName",
effects: [/* ... */]
});
Step 2: Use Recoil selector for the currently selected theme object
import { createTheme } from "@mui/material";
import { selector } from "recoil";
export const Theme = selector({
key: "Theme",
dangerouslyAllowMutability: true,
get(ctx) {
const name = ctx.get(ThemeName);
return createTheme(/* ... */);
},
});
Step 3: Add useTheme()
, useToggleTheme()
React hooks
import { useRecoilValue, useRecoilCallback } from "recoil";
export function useTheme() {
return useRecoilValue(Theme);
}
export function useToggleTheme() {
return useRecoilCallback(
(ctx) => () => {
ctx.set(ThemeName, (prev) => (prev === "dark" ? "light" : "dark"));
},
[]
);
}
Step 4: Save/restore the currently selected theme name to/from localStorage
export const ThemeName = atom<PaletteMode>({
key: "ThemeName",
effects: [
(ctx) => {
const storageKey = "theme";
if (ctx.trigger === "get") {
const name: PaletteMode =
localStorage?.getItem(storageKey) === "dark"
? "dark"
: localStorage?.getItem(storageKey) === "light"
? "light"
: matchMedia?.("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
ctx.setSelf(name);
}
ctx.onSet((value) => {
localStorage?.setItem(storageKey, value);
});
},
],
});
Step 5: Add ThemeProvider
to the top-level React component
import { ThemeProvider } from "@mui/material";
import { useTheme } from "../theme/index.js";
function App(): JSX.Element {
const theme = useTheme();
return (
<ThemeProvider theme={theme}>
{/* ... */}
</Theme>
);
}
See app/theme/index.ts
in React Starter Kit
Top comments (0)