# Next.js helpers

Source: https://capacms.com/docs/sdk/nextjs

@capacms/sdk/nextjs: cache tags, webhook revalidation, draft mode and graphql() in a server component.

```ts
import { createClient } from "@capacms/sdk/next";
import { withCache, tagsFor } from "@capacms/sdk/nextjs";

const fetchWithCache = withCache(fetch, {
  tags: tagsFor({ model: articleModelId }),
  revalidate: 60,
});

const capa = createClient({ baseUrl, apiKey, version: "2026-10-01", fetch: fetchWithCache });
```

`withCache` merges `{ next: { tags, revalidate } }` into every fetch call.
`tagsFor` builds Capa surrogate keys: `m:`, `e:`, `k:`, and `t:`, by id, and
`tagsFor({ namespace: "articles" })` builds `capa:model:articles`, the tag
for a GraphQL read (see GraphQL), and `capa:media`. `revalidateFromWebhook`
revalidates all of them for the entry and model a webhook names, and for a
media event, the file's `f:` key and `capa:media`.

Webhook revalidation pairs with the existing signature verifier:

```ts
// app/api/capa/route.ts
import { revalidateTag } from "next/cache";
import { revalidateFromWebhook } from "@capacms/sdk/nextjs";
import { verifyWebhookSignature } from "@capacms/sdk";

export async function POST(request: Request) {
  const raw = await request.text();
  const ok = await verifyWebhookSignature({
    payload: raw,
    header: request.headers.get("capa-signature") ?? "",
    secret: process.env.CAPA_WEBHOOK_SECRET!,
  });
  if (!ok) return new Response("bad signature", { status: 400 });

  await revalidateFromWebhook({
    payload: JSON.parse(raw),
    revalidateTag,
  });

  return new Response("ok");
}
```

`draftClient` is server-only. Pass Next's draft state in from the caller so the
SDK never imports `next/*`:

```ts
import { draftMode } from "next/headers";
import { draftClient } from "@capacms/sdk/nextjs";
import type { CapaQuery } from "./capa-graphql"; // written by capa-codegen --graphql

const capa = await draftClient<CapaQuery>({
  production,
  draft,
  isDraft: async () => (await draftMode()).isEnabled,
});
```

`CapaQuery` types the GraphQL builder on the client it returns, as it does
for `createClient<CapaQuery>`; `getCapaClient<CapaQuery>` and
`getPublishedClient<CapaQuery>` take it the same way. Without it each helper
returns an untyped client. `getCapaClient` sends its GraphQL reads as it
sends its REST reads, which Next does not keep, unless a call gives `tags` or
`revalidate`; then it keeps them in Next's data cache as `graphql()` does
(see The typed builder).
