React Router SPA, where should I resolve Croct content so the variant does not flash?
Our app is a React Router SPA with no server rendering at all. When a page loads I see the default hero paint first, and then the personalized one swaps in a beat later. It is a visible flash and it is bothering me.
Where should content actually resolve so visitors never see that swap? And do I need to introduce server rendering to fix it properly, or is there a way to handle it on a client-only app?
2 answers
What you are seeing is inherent to a pure client-rendered SPA: the app mounts, then fetches, so the default paints first and the variant swaps in once the response lands. That swap is the flash.
The clean fix is to resolve content on the server, where the variant is decided before the page arrives, so there is nothing left to swap on the client. That is why our server-side rendering guidance treats it as the default for personalization that has to be flash-free.
If you want to stay client-only for now, you have two honest options: hold rendering of that region until content resolves (a Suspense boundary with a neutral placeholder), or use a fallback that matches the default so the first paint and the resolved content look identical and there is no visible jump. Both are workarounds rather than a true fix, but they get you a long way.
For what it is worth, end-to-end response times are under 90 milliseconds at P95, so moving resolution to the server does not cost you a perceptible delay. The content resolution model goes through where each mode fits.
That framing helps. We will move the hero resolution server-side rather than paper over it with a matching fallback. Appreciate the detail.
Can confirm from our side. We had the same swap on a client-only SPA, moved the personalized regions to server resolution, and the flash was simply gone because the variant was already in the HTML. No placeholder gymnastics needed after that.