Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[Bug]: Hydration failed #317

Closed
bannednull opened this issue Oct 22, 2024 · 13 comments
Closed

[Bug]: Hydration failed #317

bannednull opened this issue Oct 22, 2024 · 13 comments
Labels
bug Something isn't working triage

Comments

@bannednull
Copy link

What happened?

in recent version next js 15 not working

image

Version

0.3.0

What browsers are you seeing the problem on?

No response

@bannednull bannednull added bug Something isn't working triage labels Oct 22, 2024
@kareem717
Copy link

kareem717 commented Oct 22, 2024

Hey bro,

You need to make sure that the next-theme provider is mounted before returning it, like this:

"use client";

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 [isMounted, setIsMounted] = useState<boolean>(false);

  useEffect(() => {
    setIsMounted(true);
  }, []);

  if (!isMounted) {
    return null;
  }

  return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}

I also found this library that seems to solve the problem but to be honest I have not tried it: https://www.reddit.com/r/nextjs/comments/1dy0enr/new_theming_library_specifically_for_next_with_no/

@maxwiseman
Copy link

Isn't this intended behavior? I haven't gotten this error before the Next 15 release, but the readme says to add the suppressHydrationWarning prop to your <html> tag, which fixes it for me.

https://github.com/pacocoursey/next-themes/blob/bf0c5a45eaf6fb2b336a6b93840e4ec572bc08c8/next-themes/README.md?plain=1#L95C1-L96C1

@bannednull
Copy link
Author

@kareem717 Your solution is valid but it does not work to make ssr

@kareem717
Copy link

kareem717 commented Oct 23, 2024

@kareem717 Your solution is valid but it does not work to make ssr

I mean... obviously - the whole provider is a client component either way, it's built that way. In the README it says "Note that ThemeProvider is a client component, not a server component."

Try the library I tagged - I think it might solve the issue if you want it to be SSR

@kareem717
Copy link

Isn't this intended behavior? I haven't gotten this error before the Next 15 release, but the readme says to add the suppressHydrationWarning prop to your <html> tag, which fixes it for me.

https://github.com/pacocoursey/next-themes/blob/bf0c5a45eaf6fb2b336a6b93840e4ec572bc08c8/next-themes/README.md?plain=1#L95C1-L96C1

If you want to do it like that, that seems to be a way to do so.

I haven't had this problem until next 15 as well

@maxwiseman
Copy link

@kareem717 Your solution is valid but it does not work to make ssr

I mean... obviously - the whole provider is a client component either way, it's built that way. In the README it says "Note that ThemeProvider is a client component, not a server component."

Try the library I tagged - I think it might solve the issue if you want it to be SSR

First of all, NextJS pre-renders client components on the server (source). So by returning null on the initial render, you're effectively opting out of this pre-rendering. Secondly, since the provider wraps your entire app, it will need to be rendered in order for Next to SSR or SSG any given route. Next does not run useEffect or useState when pre-rendering client components, so this will just return null, overriding the entire route's server side render. You can prove this by simply disabling Javascript in your browser's devtools and loading a SSR or SSG page. You'll see a while screen. If you simply suppress the hydration warning, you'll see all your page content. By doing this, you are opting out of SSR and SSG for your entire app.

The library you linked would solve the problem for SSR (and would actually work better for SSR), but because it uses cookies to handle theme data, it opts out of SSG, which for many is a dealbreaker. There is a prop to disable it for a route (which would enable SSG), but this isn't a very elegant solution.

This hydration error is fairly insignificant. React is able to sort everything out on its own, and we expect the className to be different on the client render because it will have access to the stored theme in localStorage, which will change the CSS class it applies. This error can and should just be ignored like it was prior to NextJS 15.

@kareem717
Copy link

@kareem717 Your solution is valid but it does not work to make ssr

I mean... obviously - the whole provider is a client component either way, it's built that way. In the README it says "Note that ThemeProvider is a client component, not a server component."

Try the library I tagged - I think it might solve the issue if you want it to be SSR

First of all, NextJS pre-renders client components on the server (source). So by returning null on the initial render, you're effectively opting out of this pre-rendering. Secondly, since the provider wraps your entire app, it will need to be rendered in order for Next to SSR or SSG any given route. Next does not run useEffect or useState when pre-rendering client components, so this will just return null, overriding the entire route's server side render. You can prove this by simply disabling Javascript in your browser's devtools and loading a SSR or SSG page. You'll see a while screen. If you simply suppress the hydration warning, you'll see all your page content. By doing this, you are opting out of SSR and SSG for your entire app.

The library you linked would solve the problem for SSR (and would actually work better for SSR), but because it uses cookies to handle theme data, it opts out of SSG, which for many is a dealbreaker. There is a prop to disable it for a route (which would enable SSG), but this isn't a very elegant solution.

This hydration error is fairly insignificant. React is able to sort everything out on its own, and we expect the className to be different on the client render because it will have access to the stored theme in localStorage, which will change the CSS class it applies. This error can and should just be ignored like it was prior to NextJS 15.

Aye fair enough. Thanks for the tip.

@gfargo
Copy link

gfargo commented Oct 28, 2024

@kareem717 & @maxwiseman, thoughts on the proposed solution in this comment? i.e. wrapping the ThemeProvider in a <Suspense> 💭

@maxwiseman
Copy link

Thoughts on the proposed solution in this comment? i.e. wrapping the ThemeProvider in a <Suspense> 💭

@gfargo That doesn't solve it for me. Does the error come back after you reload the page? Hydration errors can go away when you edit a file with the dev server running, but they sometimes come back after reloading the page.

I could be wrong, but I don't think Suspense would have any affect on ThemeProvider since it isn't a server component (and therefor isn't async), doesn't use lazy, and it doesn't currently make use of React 19's use hook. Those are the only things Suspense will wait for according to the React docs. Adding style and class attributes to a DOM element isn't async, so there's nothing for Suspense to wait on. Plus all of that code runs in an effect (as seen below), which doesn't activate Suspense according to the docs.

// Whenever theme or forcedTheme changes, apply it
React.useEffect(() => {
applyTheme(forcedTheme ?? theme)
}, [forcedTheme, theme])

@pacocoursey
Copy link
Owner

Please read the docs: https://github.com/pacocoursey/next-themes?tab=readme-ov-file#with-app

@pacocoursey pacocoursey closed this as not planned Won't fix, can't repro, duplicate, stale Nov 4, 2024
@LikeDreamwalker
Copy link

@kareem717 Your solution is valid but it does not work to make ssr

I mean... obviously - the whole provider is a client component either way, it's built that way. In the README it says "Note that ThemeProvider is a client component, not a server component."
Try the library I tagged - I think it might solve the issue if you want it to be SSR

First of all, NextJS pre-renders client components on the server (source). So by returning null on the initial render, you're effectively opting out of this pre-rendering. Secondly, since the provider wraps your entire app, it will need to be rendered in order for Next to SSR or SSG any given route. Next does not run useEffect or useState when pre-rendering client components, so this will just return null, overriding the entire route's server side render. You can prove this by simply disabling Javascript in your browser's devtools and loading a SSR or SSG page. You'll see a while screen. If you simply suppress the hydration warning, you'll see all your page content. By doing this, you are opting out of SSR and SSG for your entire app.

The library you linked would solve the problem for SSR (and would actually work better for SSR), but because it uses cookies to handle theme data, it opts out of SSG, which for many is a dealbreaker. There is a prop to disable it for a route (which would enable SSG), but this isn't a very elegant solution.

This hydration error is fairly insignificant. React is able to sort everything out on its own, and we expect the className to be different on the client render because it will have access to the stored theme in localStorage, which will change the CSS class it applies. This error can and should just be ignored like it was prior to NextJS 15.

I have a question, if next-themes is a client component, so will this actually block the RSCs? I am not sure that if we use a client component to wrap some server and client components, they will all go client component, which is SSR as well.

@LikeDreamwalker
Copy link

@kareem717 Your solution is valid but it does not work to make ssr

I mean... obviously - the whole provider is a client component either way, it's built that way. In the README it says "Note that ThemeProvider is a client component, not a server component."
Try the library I tagged - I think it might solve the issue if you want it to be SSR

First of all, NextJS pre-renders client components on the server (source). So by returning null on the initial render, you're effectively opting out of this pre-rendering. Secondly, since the provider wraps your entire app, it will need to be rendered in order for Next to SSR or SSG any given route. Next does not run useEffect or useState when pre-rendering client components, so this will just return null, overriding the entire route's server side render. You can prove this by simply disabling Javascript in your browser's devtools and loading a SSR or SSG page. You'll see a while screen. If you simply suppress the hydration warning, you'll see all your page content. By doing this, you are opting out of SSR and SSG for your entire app.
The library you linked would solve the problem for SSR (and would actually work better for SSR), but because it uses cookies to handle theme data, it opts out of SSG, which for many is a dealbreaker. There is a prop to disable it for a route (which would enable SSG), but this isn't a very elegant solution.
This hydration error is fairly insignificant. React is able to sort everything out on its own, and we expect the className to be different on the client render because it will have access to the stored theme in localStorage, which will change the CSS class it applies. This error can and should just be ignored like it was prior to NextJS 15.

I have a question, if next-themes is a client component, so will this actually block the RSCs? I am not sure that if we use a client component to wrap some server and client components, they will all go client component, which is SSR as well.

Got it: vercel/next.js#42301 (reply in thread)

@victorteokw
Copy link

Use this to prevent hydration errors: https://github.com/victorteokw/next-safe-themes

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
bug Something isn't working triage
Projects
None yet
Development

No branches or pull requests

7 participants