nextjs app router, tailwind manually trigger dark mode
To manually trigger Dark Mode in a Next.js app that uses Tailwind CSS, you'll need to do a couple of things:
<html>
or <body>
) that activates Dark Mode.Here's how you can do it step-by-step:
In your tailwind.config.js
file, ensure that Dark Mode is enabled. You can use the 'class' strategy which allows you to toggle Dark Mode by adding a class to your HTML element:
// tailwind.config.js
module.exports = {
darkMode: 'class',
// other configurations...
};
In your Next.js application, create a function to toggle Dark Mode. This function will add or remove the dark
class on the <html>
element:
// This could be in a React component or a utility file
function toggleDarkMode() {
if (typeof window !== 'undefined') {
document.documentElement.classList.toggle('dark');
}
}
Make sure to check for window
to avoid trying to access the DOM on the server-side, as Next.js does server-side rendering.
You can now use this function in a React component. For example, you could create a toggle button that allows users to switch between light and dark modes:
// components/DarkModeToggle.js
export default function DarkModeToggle() {
function handleToggle() {
toggleDarkMode();
}
return (
<button onClick={handleToggle}>
Toggle Dark Mode
</button>
);
}
Now you can import the DarkModeToggle
component into any of your pages or layout components and include it in the JSX:
// pages/index.js or components/Layout.js
import DarkModeToggle from '../components/DarkModeToggle';
export default function HomePage() {
return (
<div>
<DarkModeToggle />
{/* Rest of your page */}
</div>
);
}
When you want to style components based on the Dark Mode, you use the dark:
variant in your Tailwind CSS classes:
<div className="bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100">
{/* Content */}
</div>
In this example, bg-white
and text-gray-900
are the light theme colors, while dark:bg-gray-800
and dark:text-gray-100
are the dark theme colors. They will be applied depending on whether the dark
class is present on the <html>
element.
Now, when the user clicks the "Toggle Dark Mode" button, the dark
class will be added or removed from the <html>
element, and the styles will update accordingly.
store the setting in local storage
Storing the Dark Mode setting in localStorage allows the user's preference to persist across sessions. Here's how you can modify the toggleDarkMode
function to store the setting in localStorage:
function toggleDarkMode() {
if (typeof window !== 'undefined') {
// Check if the 'dark' class is currently set
const isDarkMode = document.documentElement.classList.toggle('dark');
// Update localStorage with the new state
localStorage.setItem('darkMode', isDarkMode ? 'enabled' : 'disabled');
}
}
Next, you'll want to read the user's preference from localStorage and apply it when the app initializes. One way to do this is to use a React effect in your layout or top-level component to set the initial theme:
import { useEffect } from 'react';
export default function AppLayout({ children }) {
useEffect(() => {
// Apply the theme when the component mounts
const darkModeSetting = localStorage.getItem('darkMode');
if (darkModeSetting === 'enabled') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}, []); // The empty array means this effect runs once on mount
return (
<div>
{/* Your site layout goes here */}
{children}
</div>
);
}
Finally, update your DarkModeToggle
component to reflect the current mode as per the localStorage value. This can be done by maintaining a state that tracks whether Dark Mode is enabled:
import { useState, useEffect } from 'react';
export default function DarkModeToggle() {
// State to hold whether Dark Mode is enabled
const [isDarkMode, setIsDarkMode] = useState(false);
useEffect(() => {
// Read the setting from localStorage when the component mounts
const darkModeSetting = localStorage.getItem('darkMode');
setIsDarkMode(darkModeSetting === 'enabled');
}, []);
const handleToggle = () => {
setIsDarkMode(!isDarkMode); // Update the state
toggleDarkMode(); // Update the class and localStorage
};
return (
<button onClick={handleToggle}>
{isDarkMode ? 'Switch to Light Mode' : 'Switch to Dark Mode'}
</button>
);
}
This setup ensures that the user's preference for Dark Mode is stored in localStorage and is used to set the initial theme when the app loads. The DarkModeToggle
component reflects the current state and allows the user to switch modes, which updates both the UI and localStorage.
is there a npm module for this
Yes, there are npm packages available that can help you manage theme switching including dark mode in your React or Next.js application. One such popular package is next-themes
. It provides an easy way to implement theme switching in Next.js, including dark mode, with support for persisting the theme across sessions using localStorage.
Here's how you can use next-themes
to implement dark mode in your Next.js application:
next-themes
PackageFirst, install the package using npm or yarn:
npm install next-themes
# or
yarn add next-themes
ThemeProvider
In your _app.js
file, wrap your application with the ThemeProvider
provided by next-themes
:
// pages/_app.js
import { ThemeProvider } from 'next-themes';
function MyApp({ Component, pageProps }) {
return (
<ThemeProvider attribute="class">
<Component {...pageProps} />
</ThemeProvider>
);
}
export default MyApp;
The attribute="class"
prop tells next-themes
to toggle the dark mode class on the <html>
tag, which is the same as Tailwind's configuration.
Use the useTheme
hook from next-themes
to toggle between themes within a component:
// components/ThemeChanger.js
import { useTheme } from 'next-themes';
export default function ThemeChanger() {
const { theme, setTheme } = useTheme();
return (
<button
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
>
Toggle Theme
</button>
);
}
next-themes
has built-in support to prevent a flash of unstyled content (FOUC) when using server-side rendering (SSR). You can pass the enableSystem
and defaultTheme
props to the ThemeProvider
to manage this behavior:
<ThemeProvider attribute="class" enableSystem={false} defaultTheme="light">
{/* Rest of your app */}
</ThemeProvider>
Setting enableSystem
to false
will prevent the system preference from overriding the stored theme, and defaultTheme
sets the initial theme before the user's preference is loaded.
By using next-themes
, you can easily manage theme switching and persist user preferences without manually handling localStorage or dealing with the potential flash of unstyled content on initial load.