react-next-theme
v1.1.1
Published
A simple and flexible theme switcher for React and Next.js, supporting light and dark modes with localStorage and system preferences.
Downloads
797
Maintainers
Readme
react-next-theme
react-next-theme
is a lightweight, customizable theme switcher for React and Next.js applications, supporting light and dark modes. It detects system preferences, applies the theme instantly, and prevents a white flash on page load.
Key Features
- Light and Dark Mode Support: Allows seamless theme switching.
- System Preference Detection: Automatically detects and applies the user’s system theme preference.
- No White Flash on Load: Instantly applies the theme for a smooth user experience.
- Easy Setup: Works seamlessly in React and Next.js (supports both Page and App Routers).
Common Setup Instructions
Install the Package
npm install react-next-theme
or
yarn add react-next-theme
Create CSS for Theme Variables
Define theme colors in a CSS file. You can either create a
theme.css
file in thepublic
folder or add these styles directly toindex.css
(React) orglobals.css
(Next.js)./* Light and dark mode theme variables */ :root { --background-color: white; --text-color: black; } html[data-theme='dark'] { --background-color: black; --text-color: white; } body { background-color: var(--background-color); color: var(--text-color); transition: background-color 0.3s, color 0.3s; }
React Setup (Create React App or Vite)
Add Theme Script to
public/index.html
To ensure the theme is applied before the app mounts, create a
theme-script.js
file in thepublic
folder with the following code:// public/theme-script.js (function() { const storedTheme = localStorage.getItem('theme'); const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; const theme = storedTheme || (prefersDark ? 'dark' : 'light'); document.documentElement.setAttribute('data-theme', theme); })();
Then, link the script in
public/index.html
:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>React App</title> <!-- Link to theme initialization script --> <script src="%PUBLIC_URL%/theme-script.js"></script> <!-- Link to CSS file --> <link rel="stylesheet" href="%PUBLIC_URL%/theme.css" /> </head> <body> <div id="root"></div> </body> </html>
Wrap Your App with
ThemeProvider
insrc/index.js
Wrap your main app component with
ThemeProvider
to enable dynamic theme switching.// src/index.js or src/main.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import { ThemeProvider } from 'react-next-theme'; ReactDOM.render( <ThemeProvider> <App /> </ThemeProvider>, document.getElementById('root') );
Use the
useTheme
Hook in ComponentsUse the
useTheme
hook to toggle themes in any component. Here’s an example for a button inApp.js
:// src/App.js import React from 'react'; import { useTheme } from 'react-next-theme'; function App() { const { theme, toggleTheme } = useTheme(); return ( <div> <h1>{theme === 'light' ? 'Light Mode' : 'Dark Mode'}</h1> <button onClick={toggleTheme}> {theme === 'light' ? 'Switch to Dark Mode' : 'Switch to Light Mode'} </button> </div> ); } export default App;
Next.js (Page Router) Setup
Theme Initialization Script in
_document.js
To apply the theme before Next.js renders the app, you can either use
getThemeScript
fromreact-next-theme
or link a separate script file.Option A: Use
getThemeScript
directly:// pages/_document.js import { Html, Head, Main, NextScript } from 'next/document'; import { getThemeScript } from 'react-next-theme'; export default function Document() { return ( <Html lang="en"> <Head> <link rel="stylesheet" href="/theme.css" /> <script dangerouslySetInnerHTML={{ __html: getThemeScript() }} /> </Head> <body> <Main /> <NextScript /> </body> </Html> ); }
Option B: Use an external script file:
Create
public/theme-script.js
:// public/theme-script.js (function() { const storedTheme = localStorage.getItem('theme'); const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; const theme = storedTheme || (prefersDark ? 'dark' : 'light'); document.documentElement.setAttribute('data-theme', theme); })();
Reference the script file in
_document.js
:// pages/_document.js import { Html, Head, Main, NextScript } from 'next/document'; import Script from 'next/script'; export default function Document() { return ( <Html lang="en"> <Head> <link rel="stylesheet" href="/theme.css" /> </Head> <body> <Script src="/theme-script.js" strategy="beforeInteractive" /> <Main /> <NextScript /> </body> </Html> ); }
Wrap Your App with
ThemeProvider
in_app.js
Wrap the main component with
ThemeProvider
to handle theme switching:// pages/_app.js import { ThemeProvider } from 'react-next-theme'; function MyApp({ Component, pageProps }) { return ( <ThemeProvider> <Component {...pageProps} /> </ThemeProvider> ); } export default MyApp;
Next.js (App Router) Setup
Theme Initialization in
app/layout.js
In Next.js 13+ (App Router), you can initialize the theme by either using
getThemeScript
or an external script file.Option A: Use
getThemeScript
directly:// app/layout.js import { ThemeProvider, getThemeScript } from 'react-next-theme'; export const metadata = { title: 'My App', }; export default function RootLayout({ children }) { return ( <html lang="en"> <head> <link rel="stylesheet" href="/theme.css" /> <script dangerouslySetInnerHTML={{ __html: getThemeScript() }} /> </head> <body> <ThemeProvider>{children}</ThemeProvider> </body> </html> ); }
Option B: Use an external script file:
Create
public/theme-script.js
as follows:// public/theme-script.js (function() { const storedTheme = localStorage.getItem('theme'); const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; const theme = storedTheme || (prefersDark ? 'dark' : 'light'); document.documentElement.setAttribute('data-theme', theme); })();
Reference the script file in
app/layout.js
:// app/layout.js import { ThemeProvider } from 'react-next-theme'; import Script from 'next/script'; export const metadata = { title: 'My App', }; export default function RootLayout({ children }) { return ( <html lang="en"> <head> <link rel="stylesheet" href="/theme.css" /> </head> <body> <Script src="/theme-script.js" strategy="beforeInteractive" /> <ThemeProvider>{children}</ThemeProvider> </body> </html> ); }
Using the useTheme
Hook to Toggle Themes
To toggle themes in components, use the useTheme
hook.
// components/Header.js
import { useTheme } from 'react-next-theme';
function Header() {
const { theme, toggleTheme } = useTheme();
return (
<header>
<button onClick={toggleTheme}>
{theme === 'light' ? 'Switch to Dark Mode' : 'Switch to Light Mode'}
</button>
</header>
);
}
export default Header;
Recommendation
For simplicity and a clean setup, using getThemeScript
is often the best choice, especially for projects where ease of integration and minimal file management are priorities. It’s also ideal for teams that prefer not to have extra files in public
or are working on smaller projects.
However, for larger projects or production-level apps where caching and modularity are important, using public/theme-script.js
can improve load times slightly and centralize your theme logic.
In summary:
- Smaller projects or simpler setups →
getThemeScript
- Larger projects or production-focused apps →
public/theme-script.js
Let me know if you’d like further customization in setup based on these considerations!
Using a Script File Instead of Inlining the Script
If you prefer to use an external script file, you can place the theme initialization script inside the public/
folder and reference it using the <Script>
component in Next.js:
1. Create public/theme-script.js
:
// public/theme-script.js
(function() {
const storedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const theme = storedTheme || (prefersDark ? 'dark' : 'light');
document.documentElement.setAttribute('data-theme', theme);
})();
2. Reference It in _document.js
or layout.js
import Script from 'next/script';
<Script src="/theme-script.js" strategy="beforeInteractive" />
This will load the theme initialization script from the public/
directory.
License
MIT License
Feel free to modify or contribute to the project. Happy coding!