I implemented a weird way to toggle dark mode in Next.js, but I don't think it's a good idea.
Unanswered
Bumble bee posted this in #help-forum
Bumble beeOP
Hi,
I am using Next.js 14 (app router) with React.js 18 and Tailwind CSS.
I wanted to create a website that lets users toggle between dark mode and light mode. Tailwind CSS allows this by using ['selector' strategy](https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually), but in order toggle dark mode with this strategy, I need to set html's class to "dark", so I export a function from
and it works. The thing is, I don't think this is a good idea. What is a better (or best) way to implement this feature?
PS. I would probably use a package in production, but I am really curious and want to learn about Next.js 😄
I am using Next.js 14 (app router) with React.js 18 and Tailwind CSS.
I wanted to create a website that lets users toggle between dark mode and light mode. Tailwind CSS allows this by using ['selector' strategy](https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually), but in order toggle dark mode with this strategy, I need to set html's class to "dark", so I export a function from
.src/libs/theme.tsxexport function changeTheme(theme: string) {
document.getElementsByTagName("html")[0].className = theme;
}and it works. The thing is, I don't think this is a good idea. What is a better (or best) way to implement this feature?
PS. I would probably use a package in production, but I am really curious and want to learn about Next.js 😄
10 Replies
American Crow
The React way of doing it would be by creating a context and a theme context provider to wrap your App.
Something like
In App.tsx or Layout.tsx
Careful, coded from memory so most likely some errors in it
Something like
const ThemeContext = React.createContext()
export function ThemeProvider({children}) {
const [currentTheme, setTheme] = React.useState("light")
return (
<ThemeContext.Provider value={{currentTheme, setTheme}}>
{children}
</ThemeContext.Provider>
)
export function useTheme() {
const context = React.useContext(ThemeContext)
if (context === undefined) {
throw new Error("useTheme must be used within a ThemeProvider. Did you forget a wrapping <ThemeProvider> ... </ThemeProvider>?")
}
return context
}In App.tsx or Layout.tsx
export default function RootLayout({children)
...
return (
<html>
...
<ThemeProvider>
...
</ThemeProvider>
</html>
)Careful, coded from memory so most likely some errors in it
Bumble beeOP
Thank you very much. The "dark" class will be added to html element by the ThemeProvider component, am I correct?
American Crow
Yea sorry i forgot
For completeness, let me add a custom hook for context retrieval
Now you can
const {theme,setTheme} = useTheme() everywhere in your App or at least everywhere below the wrapping <ThemeProvider>By everywhere i may add you have to make consumer components 'use client' since its a hook
Children of Theme Provider can still be server or client components. The theme provider itself does NOT make everything a client component (common misconception)
thats all knowledge i have

Bumble beeOP
Thank you very much. I will try it in my project.