Server component vs use client for personalization, how do I avoid a hydration mismatch?
I am deciding whether to resolve personalised content in a server component or in a client component with a hook. When I tried the client path I saw a brief flash of the default content before the personalised version appeared, and I am worried that is the road to hydration warnings.
What is the recommended pattern here for personalisation content in the App Router?
2 answers
There are two modes, and the difference you saw is exactly what separates them.
-
Server resolution with fetchContent. The content is resolved on the server and arrives inside the initial HTML. It is SEO visible, there is no client swap, and because the same markup is sent and then hydrated, there is no default-then-personalised mismatch to warn about. This is the recommended pattern for content.
-
Client hooks in a 'use client' component. The page renders with the default first, then the hook resolves and swaps the personalised content in after load. That swap is the flash you observed.
The tradeoff to be aware of is that fetchContent reads the request, so it opts the route into dynamic rendering by design. That is the expected cost of resolving per visitor on the server, not a bug. You can see the two rendering modes in the server fetchContent reference and the Next.js rendering behaviour docs.
We standardised on server resolution for anything that ships as content and it made the hydration question disappear entirely. The markup you send is the markup you hydrate, so there is nothing to reconcile. We only reach for the client hooks now on genuinely interactive bits, not for content.