useContent in our Remix + Sanity app blanks the component then swaps, expected?
We are on Remix 2.15 with @croct/plug-react 2.x, and the hero content is authored in Sanity.
I wrapped our Sanity-driven hero in useContent('home-hero') inside a Suspense boundary. Expected: hero renders with Sanity content on the server, personalized version appears seamlessly. Actual: the boundary shows the Suspense fallback during server render, then after hydration the hero pops in. So there is a visible blank-then-swap on every hard navigation.
Is this the designed behavior of the hook, or am I holding it wrong? And if it is by design, does useContent even belong in a Remix app or should the fetch move behind the loader entirely
1 answer
That is the designed behavior, not a bug in your setup. useContent relies on React Suspense and is built for client-side rendering: it suspends until the content resolves in the browser, which is why you see the fallback first and the content swapping in after load. Remix's server render cannot resolve it for you.
Two ways forward depending on how much the swap bothers you:
-
Keep the hook, but make the Suspense fallback meaningful. Pass your Sanity copy as the fallback option so the default hero renders instead of a blank, and the swap becomes copy-to-copy rather than blank-to-copy. You should always provide fallback content anyway, since it is also what renders when no experience applies or a fetch fails.
-
For genuinely flicker-free rendering, move the resolution into the loader. Call the HTTP Content API there and pass the result down as loader data, then the document arrives already personalized and there is nothing to swap. This is the pattern discussed in the Sanity + Remix integration options thread.
For a hero at the top of the page I would do option 2. The hook earns its keep lower on the page where a post-load swap is invisible.
Moved the hero to the loader and kept useContent with a Sanity fallback for a below-fold banner. Both behave exactly as you described, thanks.