Theme

Adds theme support to your Next.js app. By default implements FOUC-free light and dark theme, but can be configured to support any number of themes.

The current theme is set on the <html> element’s data-theme attribute, which can be used to apply theme-specific styles, for example:

theme.css
:root[data-theme='light'] {
  --color-primary: #000;
  --color-secondary: #333;
}
 
:root[data-theme='dark'] {
  --color-primary: #fff;
  --color-secondary: #ccc;
}

Installation

npm install @madeinhaus/nextjs-theme

Usage

To enable themes in your app, you have to add the components ThemeProvider and ThemeScript to _app.tsx and _document.tsx respectively, as follows:

ThemeProvider

ThemeProvider is a wrapper component that provides the theme context to the entire app. Via context all components have access to the current theme, the theme configuration and a function to set the theme manually.

It also keeps track of the user’s preferred theme, stores it in localStorage and updates the data-theme attribute on the <html> element.

_app.tsx
import type { AppProps } from 'next/app';
import { ThemeProvider } from '@madeinhaus/nextjs-theme';
 
const App = ({ Component, pageProps }: AppProps) => {
  return (
    <ThemeProvider>
      <Component {...pageProps} />
    </ThemeProvider>
  );
};
 
export default App;

ThemeScript

ThemeScript installs a script in the <head> element that runs on app load. It initializes the data-theme attribute on the <html> element to the user’s preferred theme (from localStorage if available, otherwise either the default or the system theme).

This happens early in the app load process, so that the theme is applied long before the app is hydrated. This means that the app will render with the correct theme on the first load, preventing a flash of unstyled content.

_document.tsx
import { Html, Head, Main, NextScript } from 'next/document';
import { ThemeScript } from '@madeinhaus/nextjs-theme';
 
export default function Document() {
  return (
    <Html>
      <Head>
        <ThemeScript />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

useTheme

In any descendent component of ThemeProvider, you can use the useTheme hook to access the current theme, the theme configuration and a function to set the theme manually.

The useTheme hook returns a ThemeContextType object with the following properties:

  • theme: string (default: value of themedDef.defaultTheme)
    The current theme, either 'light', 'dark' or 'auto'.
  • themeValue: string
    The current theme value, either 'light' or 'dark'. If the current theme is 'auto', themeValue is resolved to either 'light' or 'dark' based on the user’s system theme. This value is also set on the <html> element’s data-theme attribute.
  • themesDef: ThemeDefType
    An object containing the theme configuration.
  • setTheme: (theme: string) => void
    A function to set the theme manually. Takes a theme as an argument, either 'light', 'dark' or 'auto'.
pages/index.tsx
import { useTheme } from '@madeinhaus/nextjs-theme';
 
const Index = () => {
  const { theme, themeValue, themesDef, setTheme } = useTheme();
  return (
    <div>
      <p>Current theme: {theme}</p>
      <p>Current theme value: {themeValue}</p>
      {themesDef.themes.map(theme => (
        <button onClick={() => setTheme(theme)}>Set theme to '{theme}'</button>
      ))}
    </div>
  );
};
 
export default Index;

Customized themes

TBD