# Rendering & Data Fetching with Next.js and Agility CMS

> Source: https://agilitycms.com/docs/nextjs/next-js-and-server-side-rendering

Next.js can render a page in several ways — fully static, dynamic per request, or a mix of both. In the **App Router** (Next.js 13+, and what the [Agility Next.js Starter](https://github.com/agility/agilitycms-nextjs-starter) uses), you no longer choose a strategy with `getStaticProps` / `getServerSideProps`. Instead, **how you fetch and cache data decides how a route renders.** This guide maps the classic strategies onto the App Router and shows the recommended approach with Agility.

> Using the **Pages Router**? `getStaticProps`, `getServerSideProps`, and `getStaticPaths` still work there. Everything below is for the **App Router**, which is the recommended model for new Agility sites.

## Everything is a Server Component by default

Pages and components are **React Server Components** — they render on the server, ship zero JavaScript by default, and can `await` data directly. There's no `getStaticProps`; you fetch right inside the component:

```tsx
// app/[locale]/[...slug]/page.tsx
export default async function Page({ params }) {
  const { locale, slug } = await params
  const { page } = await getAgilityPage({ slug, locale, preview: false })
  const Template = getPageTemplate(page.templateName)
  return <Template page={page} />
}
```

## Static or dynamic? Decide per data read

| Pages Router | App Router (recommended with Agility) |
| --- | --- |
| `getStaticProps` + `revalidate` (ISR) | async Server Component + `"use cache"` + `cacheTag`, revalidated on publish |
| `getStaticPaths` | `generateStaticParams` |
| `getServerSideProps` | async Server Component that reads request data — `connection()`, `cookies()` or `draftMode()` |
| on-demand ISR (`res.revalidate`) | `revalidateTag(tag, "max")` from the publish webhook |

For a content site, the default you want is **static + tag-based revalidation** — static-fast pages that refresh the instant an editor publishes. The full setup (`cacheComponents`, `"use cache"`, `cacheTag`, the revalidate webhook) is covered in [Caching with Next.js and Agility](/docs/nextjs/caching-with-next-js-and-agility).

## Prerender every CMS page: `generateStaticParams`

Replace `getStaticPaths` with `generateStaticParams`, driven by the Agility sitemap:

```tsx
export async function generateStaticParams() {
  const sitemap = await getSitemapFlat({ locale: "en-us", preview: false })
  return Object.values(sitemap).map((node) => ({
    slug: node.path.split("/").filter(Boolean),
  }))
}
```

## Force dynamic (per-request) rendering when you need it

Sometimes a page genuinely must render per request — personalization, auth, or reading the incoming request. Opt a single read into request time with `connection()`:

```tsx
import { connection } from "next/server"

export default async function Page() {
  await connection()            // this render is now dynamic
  const data = await fetchPerRequestData()
  // ...
}
```

> ⚠️ **There is no `export const dynamic = "force-dynamic"` escape hatch here.** Route segment configs — `dynamic`, `revalidate`, `runtime` and `dynamicParams` — are **rejected** when `cacheComponents` is enabled, and leaving one in place fails the build. `connection()` at the top of the component is the replacement, and it's better: it marks the *read* as request-time rather than condemning the whole route, so everything above it still prerenders.

Reading `cookies()`, `headers()`, or `draftMode()` also opts a route into dynamic rendering automatically.

## Preview is always dynamic

Agility **preview / draft** rendering must never be cached, so editors always see their latest work. The pattern is a preview-aware fetch that calls `connection()` and skips the cache — see the preview branch in the [caching guide](/docs/nextjs/caching-with-next-js-and-agility). Preview itself is toggled with Next's `draftMode()`.

## Streaming & Partial Prerendering

With Cache Components enabled, Next prerenders the **static shell** of a route and **streams** the dynamic parts at request time. Wrap any request-time or slow region in `<Suspense>` so the rest of the page ships instantly:

```tsx
<Suspense fallback={<HeaderSkeleton />}>
  <SiteHeader />   {/* fetches per-request / preview data */}
</Suspense>
```

This is also what keeps a `connection()` call from turning the **whole** route dynamic — only the Suspense boundary around it becomes request-time.

## Recommendation

For an Agility-powered content site, **default to static Server Components with `"use cache"` + tag-based revalidation.** You get static-fast pages, instant updates when editors publish, and you reach for `connection()` only where a page truly depends on the incoming request. For the complete caching setup, read [Caching with Next.js and Agility](/docs/nextjs/caching-with-next-js-and-agility).
