Next.js Discord

Discord Forum

Hydration error with SSG as soon as even a static variable is used

Unanswered
American black bear posted this in #help-forum
Open in Discord
Avatar
American black bearOP
I'm having a very weird issue since I moved to doing Static Site Generation. Now, there are places where as soon as I use a variable in my template, such as a static {staticVar} will cause a hydration error. I've tried everything I could think of, like setting it to 0 and only changing its value from useEffect, changing from a client component to a server component, passing the value from the server component as a prop to the client component, etc. Nothing works.

In fact, even if I do something as simple as taking my copyright year and changing 2024 to {2024}, the hydration error occurs. So essentially, as soon as a variable exists, the hydration error rears its ugly head and I am uncertain what else to try at the moment.

I should also mention that something, my prettier adds some {' '} to retain some spaces. All of these have caused the same hydration error thus far as well.

I would appreciate any insights as to why this may be happening.

60 Replies

Original message was deleted
Avatar
American black bearOP
What do you mean? I haven't received anything apart from your message just now.
Avatar
American black bearOP
Here's a more concrete example.

This does not cause a hydration error:
<span className="text-sm sm:text-center">
  &copy; 2024 Company. All Rights Reserved.
</span>


This causes a hydration error:
<span className="text-sm sm:text-center">
  &copy; {2024} Company. All Rights Reserved.
</span>


The idea being that the year should be dynamic but even just making it a static variable breaks hydration.
Avatar
American black bearOP
I can't use a variable at all. The hydration error happens as soon as it's a variable. The year is the same in the source as what it should be. I'm resorting to trying string interpolation now to see if I can force the server and client to see the same thing.

I'm wondering if at the moment, using a straight variable will do the interpolation at the server level and the client has the JSX, seeing a difference. One hint that this could be an issue is that html entities were also giving me this problem, such as the copyright symbol being originally written as &copy;.
Avatar
u need to share more information actually
does it happen on dev? what actual error do u get
take a screenshot of the error
usually nextjs shows exactly where the hydration error happens
Avatar
American black bearOP
on dev, everything is dynamic so this problem does not occur. I am trying to get everything to work as SSG to gain the power of the CDN and edge for performance. So at the moment, I have to deploy to my sandbox every time to test because this doesn't occur in dev where things are always dynamic.
It doesn't show when I deploy statically because it's set as a production build
Avatar
the error doesnt show up?
nothing in the console?
where are u getting the error then?
Avatar
American black bearOP
Image
it only says there's a hydration error and points online to the message but doesn't show the trace because it's a production build (only place where it happens when things are statically served)
Avatar
and if u click on the link, it tell u that it is a hydration error right?
Avatar
American black bearOP
yes
Avatar
where do u think the error could happen in your code?
u shared some snippets
Avatar
American black bearOP
it happens in many places but the easiest example to follow is the copyright one
Avatar
so basically, on the page where the copyright component is, if u remove the dynamic part, no error?
Avatar
American black bearOP
if statically written as 2024, no problem. If made into a static variable {2024}, hydration error.
Avatar
American black bearOP
yes, that's a client component since it's the footer and requires knowing the user's logged in status to determine some links
Avatar
so basically that one component is your footer?
can u share the code
take a screenshot
Avatar
American black bearOP
with the copyright, yes. But this happens in a bunch of other places, including some legal pages where my prettier simply added some {' '} to retain some spaces while wrapping
I don't think the code will really help because there's really no reason why 2024 and {2024} would be any different, yet the latter gives a hydration error
Avatar
i cant really help u if i dont know how your code looks like
its like im trying to guess for u what the error is without having something to work with šŸ˜‚
Avatar
American black bearOP
The snippet I sent for the copyright is the only part that matters though. My entire component works fine if I do 2024 but as soon as it's a variable {2024}, the hydration error occurs so regardless of anything else in the component, that is 110% the culprit.
Avatar
well i know that, u dont have to tell me
then share a minimum reproduction repo if u dont want to share the code
Avatar
American black bearOP
trying to find a smaller example. The footer is 300 lines
Avatar
if u think, u correctly defined the variable without doing some weird manipulation between rendering and defining u can just make a small reproduction of that component and share it with stackblitz or whatever
cause the error can also happen if the value is set before the client component renders and changes while hydration
Avatar
American black bearOP
I'm gonna temporarily slim down the footer and show you a reproducible example that's easier to use
yeah, but in this case, the value is static and cannot change, yet still yields the error
it's 2024 regardless if written statically or in a variable
is your 2024 var a string?
that u define
Avatar
American black bearOP
I only tried as a number
Avatar
that could the issue be
before u render that, convert it to a string
u can call toString() directly in tsx
Avatar
American black bearOP
I slimmed down the footer as an example and I'm just confirming that the issue is still reproducible. I'll try with number and string as well while I test this.
Avatar
American black bearOP
I just remember though that I did try converting to a string on another component and it didn't help. I'll try again here though just for completeness
Alright so here's the slimmed down footer where I just confirmed that the problem is still ocurring:
'use client'

import * as React from 'react'
import { useTheme } from 'next-themes'

export default function Footer(): React.ReactElement {
  const [_apiUrl, setApiUrl] = React.useState<string>(`/v1/docs`)

  const { systemTheme, theme } = useTheme()
  const currentTheme = theme === 'system' ? systemTheme : theme

  React.useEffect(() => {
    setApiUrl(`/v1/docs?theme=${currentTheme === 'dark' ? 'dark' : 'light'}`)
  }, [currentTheme])

  return (
    <footer className="sticky top-[100vh] bg-neutral-100 dark:bg-neutral-900">
      <span className="text-secondary-text dark:text-secondary-text-dark text-sm sm:text-center">
        &copy; {2024} Company. All Rights Reserved.
      </span>
    </footer>
  )
}


I left some state stuff in there to keep it almost the same as the full component but none of those should actually impact anything since they all work fine if I change {2024} to 2024.
Will try now with {Number(2024).toString()}
Avatar
@American black bear Will try now with `{Number(2024).toString()}`
Avatar
that will probably work
Avatar
American black bearOP
It's being deployed now for testing but I tried a string on another component and the hydration error still occurred.
Confirmed. Even as a string, the hydration error still occurs.
What I suspect the most at the moment is that because of SSG, the variables get interpolated and sent to the client that way. So I think the client is actually receiving/seeing Ā© 2024 Company. All Rights Reserved. from the server and then compares to &copy; {2024} Company. All Rights Reserved. and sees a difference.

If that's the case, it's just unclear to me how the client is ever supposed to be able to take over without a hydration error when the initial file has been statically generated as SSG and will never have variables in it since they'll all already have been interpolated.
Avatar
American black bearOP
This works:
{`Ā© ${new Date().getFullYear()} `}


I suspect I may have been on the right track with what I said above. I think the {2024} is simply causing an interpolation to happen and because of the, it's not the actual 2024 value that differs, it's the html entity &copy; vs actual symbol Ā©