Open TheOrcDev opened 1 week ago
Hey, as it is marked on the doc, you should add suppressHydrationWarning
on your html tag :
import { ThemeProvider } from "@/components/theme-provider"
export default function RootLayout({ children }: RootLayoutProps) {
return (
<>
<html lang="en" suppressHydrationWarning>
<head />
<body>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
</body>
</html>
</>
)
}
Hey, as it is marked on the doc, you should add
suppressHydrationWarning
on your html tag :import { ThemeProvider } from "@/components/theme-provider" export default function RootLayout({ children }: RootLayoutProps) { return ( <> <html lang="en" suppressHydrationWarning> <head /> <body> <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange > {children} </ThemeProvider> </body> </html> </> ) }
Thank you!
So this is not connected to Next.js 15? It's still that old problem we had with ThemeProvider
?
Hey, as it is marked on the doc, you should add
suppressHydrationWarning
on your html tag :import { ThemeProvider } from "@/components/theme-provider" export default function RootLayout({ children }: RootLayoutProps) { return ( <> <html lang="en" suppressHydrationWarning> <head /> <body> <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange > {children} </ThemeProvider> </body> </html> </> ) }
Thank you!
So this is not connected to Next.js 15? It's still that old problem we had with
ThemeProvider
?
Here is the real fix :
'use client'
import * as React from 'react'
const NextThemesProvider = dynamic(
() => import('next-themes').then((e) => e.ThemeProvider),
{
ssr: false,
}
)
import { type ThemeProviderProps } from 'next-themes/dist/types'
import dynamic from 'next/dynamic'
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}
You have to dynamically import the theme provider from next-theme
and you can now remove the suppressHydrationWarning
from your <html>
tag
Hey, as it is marked on the doc, you should add
suppressHydrationWarning
on your html tag :import { ThemeProvider } from "@/components/theme-provider" export default function RootLayout({ children }: RootLayoutProps) { return ( <> <html lang="en" suppressHydrationWarning> <head /> <body> <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange > {children} </ThemeProvider> </body> </html> </> ) }
Thank you! So this is not connected to Next.js 15? It's still that old problem we had with
ThemeProvider
?Here is the real fix :
'use client' import * as React from 'react' const NextThemesProvider = dynamic( () => import('next-themes').then((e) => e.ThemeProvider), { ssr: false, } ) import { type ThemeProviderProps } from 'next-themes/dist/types' import dynamic from 'next/dynamic' export function ThemeProvider({ children, ...props }: ThemeProviderProps) { return <NextThemesProvider {...props}>{children}</NextThemesProvider> }
You have to dynamically import the theme provider from
next-theme
and you can now remove thesuppressHydrationWarning
from your<html>
tag
Amazing! That's exactly what I needed!
Thank you! You're a true web dev warrior!
This solution is effective in preventing errors during SSR, but it isn’t an "elegant" approach. Dynamically loading the next-themes library can lead to undesired outcomes, such as a "snapshot of the wrong theme" at the start.
This solution is effective in preventing errors during SSR, but it isn’t an "elegant" approach. Dynamically loading the next-themes library can lead to undesired outcomes, such as a "snapshot of the wrong theme" at the start.
That means that there is somewhere an issue, either with next-theme or with NextJs.
Now I think, I preffer having a "snapshot of the wrong theme" instead of suppressing all my hydration warning and troubleshouting during hours because nobody told me that i have put a <div>
into an <p>
in development mode.
But for the build, you're right, you could just suppress hydration warnings by using env like :
<html suppressHydrationWarning={process.env.NODE_ENV === 'production'}>
<>{children}</>
</html>
And then in your theme-provider.tsx
:
'use client'
import * as React from 'react'
import dynamic from 'next/dynamic'
import { type ThemeProviderProps } from 'next-themes/dist/types'
import { ThemeProvider as StaticProvider } from 'next-themes'
const DynProvider = dynamic(
() => import('next-themes').then((e) => e.ThemeProvider),
{
ssr: false,
}
)
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
const NextThemeProvider =
process.env.NODE_ENV === 'production' ? StaticProvider : DynProvider
return <NextThemeProvider {...props}>{children}</NextThemeProvider>
}
'use client' import * as React from 'react' const NextThemesProvider = dynamic( () => import('next-themes').then((e) => e.ThemeProvider), { ssr: false, } ) import { type ThemeProviderProps } from 'next-themes/dist/types' import dynamic from 'next/dynamic' export function ThemeProvider({ children, ...props }: ThemeProviderProps) { return <NextThemesProvider {...props}>{children}</NextThemesProvider> }
Thanks! It seems to be the correct solution in general, although I must clarify that something different has worked better for me, understanding that it slows down rendering, in small projects I have not noticed any difference. On the other hand, using dynamic with ssr: false has generated a small flash at the start that I prefer to avoid. I am quite new to this, I am probably making mistakes, so I would like to read them.
"use client";
import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { type ThemeProviderProps } from "next-themes/dist/types";
import { useEffect, useState } from "react";
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
setIsLoaded(true);
}, []);
if (!isLoaded) {
return null;
}
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}
'use client'
import * as React from 'react'
const NextThemesProvider = dynamic(
() => import('next-themes').then((e) => e.ThemeProvider),
{
ssr: false,
}
)
import { type ThemeProviderProps } from 'next-themes/dist/types'
import dynamic from 'next/dynamic'
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}
i tried that it works fine, but when i am using next intl, with this fix, i got non sense bug. assume i have 2 languages (en, de) if i have "de" selected and refresh the page it's fine, but if i switch to "en" and switch back to "de" i only see blank white screen. this not happening if i refresh the page with the default language in my case "en". my best guess next intl is changing the html props to add language like this
const { locale } = await params;
return (
<html lang={locale}>
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<MainProvider>{children}</MainProvider>
</body>
</html>
);
and next themes is changing over it, or vise versa. that introduce this conflict, i checked the terminal and the pages was requested exactly as it supposed to no errors on console, no errors on terminal
if (!isLoaded) { return null; }
that's not a good solution either. That useEffect will not run until after the component is mounted and painted, meaning you will initially see a blank screen for hundred or so milliseconds before NextThemesProvider
can run. It's not an ideal. The whole point of SSR is to avoid this type of rendering delay. According to next-theme's readme the suppressHydrationWarning
prop on the html tag is mostly innocuous because
"This property [
suppressHydrationWarning
] only applies one level deep, so it won't block hydration warnings on other elements."
Describe the bug
Implementing dark mode, and putting
ThemeProvider
into the layout is making hydration error in newest version of Next.js (15.0.1).Affected component/components
ThemeProvider
How to reproduce
ThemeProvider
component.Codesandbox/StackBlitz link
https://ui.shadcn.com/docs/dark-mode/next
Logs
Before submitting