Theming and Dark Mode
In this page:
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<div className={theme}>{children}</div>
</ThemeContext.Provider>
);
}
Storing the Current Theme in Context
A ThemeContext, holding the current theme name (like light or dark) and a function to change it, lets any component in the app read and react to the current theme, without manually passing it down through props everywhere.
Example: Storing the Current Theme in Context
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const ThemeContext = React.createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = React.useState("light");
return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>;
}
function ThemedBox() {
const { theme } = React.useContext(ThemeContext);
return <div style={{ background: theme === "dark" ? "#222" : "#fff", color: theme === "dark" ? "#fff" : "#000", padding: "16px" }}>Current theme: {theme}</div>;
}
function App() {
return <ThemeProvider><ThemedBox /></ThemeProvider>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Toggling Between Themes
A toggle button reads the current theme from context and calls setTheme with the opposite value, letting the user switch themes on demand. Every component reading the theme via useContext automatically re-renders with the new value.
Example: Toggling Between Themes
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const ThemeContext = React.createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = React.useState("light");
const toggle = () => setTheme(t => t === "light" ? "dark" : "light");
return <ThemeContext.Provider value={{ theme, toggle }}>{children}</ThemeContext.Provider>;
}
function ThemeToggle() {
const { theme, toggle } = React.useContext(ThemeContext);
return <button onClick={toggle}>Switch to {theme === "light" ? "dark" : "light"} mode</button>;
}
function App() {
return <ThemeProvider><ThemeToggle /></ThemeProvider>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Using CSS Variables for Theme Values
Instead of computing colors with JavaScript ternaries in every component, CSS custom properties (variables) can be defined once and swapped at the document root, letting plain CSS rules automatically pick up the new values everywhere they're used.
Example: Using CSS Variables for Theme Values
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
const [dark, setDark] = React.useState(false);
const vars = dark ? { "--bg": "#222", "--fg": "#fff" } : { "--bg": "#fff", "--fg": "#000" };
return (
<div style={{ ...vars, background: "var(--bg)", color: "var(--fg)", padding: "16px" }}>
<button onClick={() => setDark(!dark)}>Toggle theme (CSS variables)</button>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Hardcoding colors directly in components instead of referencing theme variables, making a later theme switch require editing every component.
- Not persisting the user's theme choice (e.g. to localStorage), so it resets on every page reload.
- Forgetting to also update things outside of individual components, like the page's overall background color.
- Theming lets an app switch between visual variants (like light/dark mode) using shared variables rather than hardcoded values.
- A common approach combines Context (to hold the current theme) with CSS variables or a theme object.
- Persisting the chosen theme (e.g. localStorage) keeps it consistent across page reloads.
- CSS custom properties (variables) can be swapped at the document root to restyle the whole app at once.
CSS custom properties supported in all modern browsers; Context API stable since React 16.3.
Chapter Quiz — Complete all 8 topics to unlock
0/8 topics done
Complete these topics first: