Solving expected hydration errors without flickering?

I’m working on a new i18n library for Next.js.

Sometimes there is information that can only be known on the client-side, e.g. a number n which determines whether to render a singular or plural.

In my library, for a plural you would write something like:

<Plural n={n} singular={<>I have <Num value={n}/> book.</>}>
   I have <Num value={n}/> books.
</Plural>

Because the pluralization and number formatting logic changes depending on the value of n, if is being used on the client-side, there needs to be a useEffect() somewhere or there will be a hydration mismatch. Unfortunately, adding a useEffect() causes flickering/blinking since there has to be a render cycle before the correct plural can be displayed.

Using next/dynamic also causes a flickering effect. Using suppressHydrationWarning only works one level deep, so fails in situations where you want to render alternate branches (like plurals).

Is there a way around this flickering?

Hey there, I think your best bet is to use a Suspense boundary to handle the transition from server to client. At a high level it works like this:

The server renders a fallback state - often you see a loading skeleton but it could just as easily be a placeholder message or even blank space (remember to specify size to avoid layout shift!!). Once the client is ready, e.g. the component “unsuspends”, it takes over and renders the real UI.

The difference is that Suspense works with React’s internal scheduling and reconciliation systems, while useEffect can only handle updates after the initial render. By skipping the initial render, you avoid hydration errors that happen when the client doesn’t have what it needs yet but is forced to put something on the screen.

What’s going to also make a difference in your specific case is what you use as a fallback. A loading skeleton can be useful for large, complex components that actually take a while, but in your case you’ll still need to figure out how to avoid the transition from fallback to the real content, since the delay could be quite short. A couple ways to handle this:

  • Start with 0 opacity and fade in quickly - play with the timing and easing functions to get it to feel right, you may still see flickering at first but this should be avoidable with some tweaking
  • Make your fallback visually identical to one of your states - this will be tricky if you expect more plural than singular elements, since by definition you can’t pre-render the plural state

I’m sure there are plenty of other techniques out there as well, feel free to experiment and figure out what works for your library. Anyway, I hope this helps! Let me know if anything is unclear.

2 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.