Repo of the Day
pacocoursey/next-themes
Published: Dec 8, 2024
Open repository ↗Perfect Next.js dark mode in 2 lines of code. Support System preference and any other theme with no flashing
Summary
next-themes is a TypeScript library that adds theme support to React applications, with a focus on Next.js. It wraps the app in a ThemeProvider and applies the selected theme to the html element before the page renders, which prevents the common flash of incorrect theme that often appears with server-rendered pages. It supports any number of named themes, syncs the choice across browser tabs, and can read the operating system's color-scheme preference.
What it is useful for
This library is useful when you need to add dark mode (or any number of named themes) to a Next.js app and want correct behavior under SSR and SSG. The README documents support for both the pages/ router and the Next.js 13 app/ directory, and states it also works with Gatsby and Create React App from version 0.3.0 onward. It covers several recurring pain points:
- Reading
prefers-color-schemeso the site follows the OS theme by default. - Forcing a single page to a specific theme, for example a marketing page that is always dark.
- Using the same UI with Tailwind's
dark:classes by switching theattributetoclass, or with any CSS-in-JS solution, because the library only manipulates a DOM attribute and leaves styling to you. - Avoiding the flash of wrong theme on first paint by injecting a small script that sets the attribute before React hydrates.
How engineers can use it
Installation is documented as npm install next-themes or yarn add next-themes.
For the pages/ router, wrap the app in _app.js:
import { ThemeProvider } from 'next-themes'
function MyApp({ Component, pageProps }) {
return (
<ThemeProvider>
<Component {...pageProps} />
</ThemeProvider>
)
}
export default MyApp
For the app/ directory, place ThemeProvider inside app/layout.jsx, mark the layout as a client component, and add suppressHydrationWarning to the html element to silence the expected hydration warning.
The default attribute is data-theme, so styling is as simple as:
:root { --background: white; }
[data-theme='dark'] { --background: black; }
Components read or change the current theme with the useTheme hook. The README warns that values from useTheme are undefined on the server, so any UI that depends on the current theme must wait for a mounted flag or be loaded with ssr: false via next/dynamic.
Documented limitations and gotchas:
- The theme attribute is applied to the
htmlelement only; setting it onbodyor other elements is not supported. - In Next.js dev mode, the README says the page may still flash; flashing only disappears in production builds.
forcedThememakessetThemea no-op, so theme switching UI should be disabled when it is set.- Passing a custom
themesarray overrides the default['light', 'dark']list, so those need to be included explicitly if still wanted.