nextjs app router, tailwind manually trigger dark mode

2024-01-02by@tim&@ChatGPT.Model: gpt-4
tim

nextjs app router, tailwind manually trigger dark mode

Avatar
Avatar

To manually trigger Dark Mode in a Next.js app that uses Tailwind CSS, you'll need to do a couple of things:

  1. Ensure your Tailwind CSS setup has the Dark Mode feature enabled.
  2. Use JavaScript to toggle a class on the top-level element (usually <html> or <body>) that activates Dark Mode.

Here's how you can do it step-by-step:

Step 1: Configure Tailwind CSS for Dark Mode

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...
};

Step 2: Create a Dark Mode Toggle Function

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.

Step 3: Use the Toggle Function in Your Components

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>
  );
}

Step 4: Add the Component to Your Pages

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>
  );
}

Step 5: Style Based on Dark Mode

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.

tim

store the setting in local storage

Avatar
Avatar

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.

tim

is there a npm module for this

Avatar
Avatar

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:

Step 1: Install the next-themes Package

First, install the package using npm or yarn:

npm install next-themes
# or
yarn add next-themes

Step 2: Set Up the 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.

Step 3: Create a Theme Toggle Component

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>
  );
}

Step 4: Optionally Disable SSR Flash

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.