Does personalizing Storyblok content on Vercel hurt our TTFB?
Storyblok + Next.js on Vercel. Current lab numbers: TTFB 180ms, LCP 1.4s, all green. I protect these numbers.
Before adopting server-side personalization I need two things quantified:
- What latency does the content fetch add per request? A real P95, not a marketing average.
- What happens to routes that are currently static? we worked hard to keep most of the site static
Not interested in client-side approaches, we tested one last year and the swap was visible on 3G throttling.
2 answers
Concrete numbers for both questions.
-
End-to-end response times are under 90 milliseconds at P95. That is the full round trip for content resolution, measured end to end, not just server processing time. On a route with a 180ms TTFB budget, the fetch happens within your server render, so the addition is bounded by that figure and typically well below it.
-
This is the tradeoff to plan deliberately: fetchContent reads the request, which opts the route into dynamic rendering. A static route that calls it stops being static. The way to protect your current output is to keep fetchContent off routes meant to stay static and personalize only the routes where it pays for itself, typically the homepage and other high-intent pages. There is a longer discussion of this behavior in fetchContent forcing dynamic rendering in Next.js.
On setup: Croct is on the Vercel Marketplace with a one-click Next.js integration that sets the environment variables automatically, so the wiring itself will not cost you an afternoon.
Since you ruled out client-side approaches already: server-side resolution means the personalized variant is in the initial HTML, so the 3G swap you saw last year does not exist in this model.
Field report since you asked for real numbers: we run Storyblok + Croct on Vercel, personalized homepage and two landing pages, rest of the site static. Our TTFB on the dynamic routes went from ~150ms (ISR hit) to ~220-240ms at P75 measured from EU traffic. P95 delta was around 100ms. Static routes obviously unchanged since they never call fetchContent.
Whether that trade is worth it depends on the route. For our homepage the conversion lift justified it immediately, for deep content pages we just left them static.
This is the kind of data I wanted, thanks. Homepage-only rollout first then, measuring before and after.