Next.js Discord

Discord Forum

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
Open in Discord
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 .src/libs/theme.tsx
export 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
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
:fine:
Bumble beeOP
Thank you very much. I will try it in my project.