Where do I call fetchContent for a Storyblok-mapped slot in the App Router?
App Router site rendering Storyblok stories, and I have mapped a hero block to a Croct slot. I want the personalized content to arrive server-rendered so there is no flash of the default block on first paint.
Where should I actually call the fetch so I get that behaviour? I would rather resolve it on the server than hydrate and swap, but I am not sure which layer this belongs in.
2 answers
Do it in the server component that renders the block, with fetchContent from @croct/plug-next/server. Pass the Storyblok block content as the fallback and the context with your attributes:
import {fetchContent} from '@croct/plug-next/server';
export default async function Hero({storyblokBlock}) { const {content} = await fetchContent('home-hero@1', { fallback: storyblokBlock, context: { attributes: { // whatever you use for targeting }, }, });
return <HeroView {...content} />;}Because it resolves on the server, the HTML that reaches the browser already has the right variant, so it stays SEO-visible and there is nothing to swap client-side. One thing I would do is pin the slot version like home-hero@1 so preview matches production instead of drifting to latest. More background in serving Storyblok blocks without the flash.
That worked, first paint is already the variant. Pinning the version also fixed a preview mismatch I had not connected to this. Thank you.
Just flag the trade-off before you sprinkle it everywhere: fetchContent reads the request, so any route that calls it opts into dynamic rendering. That is fine for a personalized homepage, but do not put it on routes you need to keep static. The details are in how fetchContent forces dynamic rendering.