A NextJS wrapper component which provides a useTheme hook as well as dealing with theme changes and the dreaded flash of incorrect theme
Table of Contents
Install the package as a dependency
npm install next-use-theme
or with yarn
yarn add next-use-theme
We need to wrap the component tree with our component and so it's recommend we do this in the _app file. If you haven't already got one, create a custom _app and wrap the Component with our ThemeProvider.
Since both the ThemeProvider and ThemeScript require the same props to function properly, it's recommended to store these in a global variable.
This example uses a config stored in _app but it can also be placed into its own file.
// Example pages/_app.js
import { ThemeProvider } from "next-use-theme";
export const config = {
themes: ["dark", "light", "tech"]
}
function MyApp({ Component, pageProps }) {
return (
<ThemeProvider {...config}>
<Component {...pageProps} />
</ThemeProvider>
);
}
export default MyApp;
To avoid the FOIT (Flash of Incorrect Theme) we need to inject a script into the Head to run before React/Nextjs. We do this with a custom _document
// Example pages/_document.js
import { Html, Head, Main, NextScript } from 'next/document'
import { ThemeScript } from 'next-use-theme'
import { config } from './_app';
export default function Document() {
return (
<Html>
<Head >
<ThemeScript {...config} />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
Then we you can use our hook useTheme() to access and change the current theme
// Example hook usage
import { useTheme } from "next-use-theme";
function ThemeButtons() {
const { setTheme, toggle, theme } = useTheme();
return (
<>
<h1>Current theme: {theme}</h1>
<button type="button" onClick={toggle}>
Toggle theme
</button>
<button type="button" onClick={() => setTheme("light")}>
Light theme
</button>
<button type="button" onClick={() => setTheme("dark")}>
Dark theme
</button>
</>
);
}
Themes are the props for both the ThemeProvider and ThemeScript.
These are all optional
themes
: string[]- List of all available theme names, defaults to the two props [lightTheme, darkTheme], eg ["lightTheme", "darkTheme"]
- Default: [lightTheme, darkTheme, "system"]
defaultTheme
: string- Default theme, this must also exist in themes
- Default: "system"
lightTheme
: string- Light theme name
- Default: "light"
darkTheme
: string- Dark theme name
- Default: "dark"
toggleThemes
: string[]- Themes for the toggle to loop through
- Default: [lightTheme, darkTheme]
attribute
: `data-${string}` | "class"- The HTML attribute to be set
- Default: "class"
mediaQuery
: boolean- Use media query to toggle the theme between light and dark. If true and no storage handlers find a theme, next-theme falls back onto the media query
- Default: true
colorScheme
: boolean- Should we set the meta tag colorScheme, if default theme is dark we set it to dark light otherwise light dark
- Default: true
onChange
: (theme: string, resolvedTheme: string) => void- Handle the theme change yourself. Setting this disables next-theme from setting the attribute
- Default: undefined
storageHandlers
: Handler[]- The array of storage handlers. Used to customize where we get and store our theme information
- Default: [localStorage()]
respectHandlerOrder
: boolean- If true, when a handler changes we only use the value of the first handler to yield a valid theme. If false, we accept the new value if valid
- Default: false
Returns for the useTheme Hook
theme
: string- The current theme set
setTheme
: (theme: string) => void- Change the current theme, only themes listed to the provider are accepted
toggle
: () => void;- Toggle the theme, this goes through the toggle themes listed to the provider
resolvedTheme
: string- Same as theme unless system theme is set, then shows 'system' while theme holds dark/light (set by provider)
This project is written in TypeScript and therefore fully supports it.
I built this project to make theme handling much easier and hassle free. It's amazing how hard it is to handle theme changes so I hope this project makes your life easier
- No horrible flash of incorrect theme (FOIT)
- Easy to use hook and wrapper
- Highly customizable
- Lightweight
I also maintain a library of animated toggles
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.
Distributed under the MIT License. See LICENSE
for more information.