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:
: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.
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.
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'sdata-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'
.
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