Prerendering
Learn how to serve dynamic content on prerendered and statically generated pages.
If you prerender your Next.js site for performance, you might worry that you can no longer run A/B tests, tailor content to each visitor, or update it from your CMS. You can. This guide explains why prerendering and dynamic content seem to be at odds, and shows the two ways to combine them so you can pick the right one for each page.
Static vs. dynamic content
Next.js renders pages statically at build time by default. That is great for performance, but it also means the HTML is the same for everyone the moment it is generated.
Dynamic content, on the other hand, is decided at request time, whether that is an A/B test variant, content tailored to the visitor, or the latest content from your CMS. A fully prerendered page is frozen by the time the visitor sees it, so on its own it cannot adapt on the fly.
The good news is that you do not have to choose between the two for your whole site. Next.js lets you render each route statically or dynamically, and the SDK ships both server-side functions and client-side hooks. So you keep prerendering everywhere it makes sense and change the approach only for the pages that need dynamic content.
Choosing an approach
There are two ways to serve dynamic content on a page without giving up prerendering across the rest of your site.
| Server-side rendering | Client-side rendering | |
|---|---|---|
| Final content on first load | ||
| No content flicker | ||
| Visible to crawlers | ||
| Served as a static file | ||
| Runs without a server |
For most cases we recommend rendering these pages on the server. The experience is smoother, there is no content flicker, and the content is visible to search engines. Reach for client-side rendering when a page must remain a fully static file, for example when it is served from a CDN with no server at all.
Render on the server
To serve dynamic content on the server, fetch it with fetchContent from @croct/plug-next/server. Because it reads the incoming request, Next.js renders the route dynamically, so the right content is decided per request and is already in the first HTML response.
import {fetchContent} from '@croct/plug-next/server';
// Optional: make dynamic rendering explicit.export const dynamic = 'force-dynamic';
export default async function HomePage() { const {content} = await fetchContent('home-hero');
return ( <section> <strong>{content.title}</strong> <p>{content.subtitle}</p> </section> );}The rest of your site stays prerendered, so only the pages that need it are rendered on the server.
Render on the client
When a page must stay static, keep it prerendered and load the content in the browser. The page ships static, and the content updates right after it appears.
Use the client hooks and components from @croct/plug-next inside a 'use client' component, with your app wrapped in the <CroctProvider> added during integration.
These hooks fetch in the browser and need an initial value, or a <Suspense> boundary, to render during prerendering. The initial content is shown to visitors and crawlers until the content loads.
'use client';
import {useContent} from '@croct/plug-next';
export function HomeHero() { const {content} = useContent('home-hero', { initial: { title: 'Welcome to Croct!', subtitle: 'The easiest way to personalize your application.', }, });
return ( <section> <strong>{content.title}</strong> <p>{content.subtitle}</p> </section> );}Do not call fetchContent on a route you want to keep static. Because it reads the request, it opts the route into dynamic rendering. To serve dynamic content on a static route, use the client hooks and components from @croct/plug-next instead.