Use Cases (Updated: 6/2/2026)

Claude Code and Contentful CMS: a Practical Integration Guide

Integrate Contentful with Claude Code, Next.js, Astro, preview mode, types, locales, and ISR without token mistakes.

Claude Code and Contentful CMS: a Practical Integration Guide

Start with the integration boundary

Contentful is a headless CMS: editors manage structured content in Contentful, while your website renders that content through APIs. Claude Code is useful here because a Contentful integration is not just a single SDK call. It touches content models, environment variables, preview tokens, locale routing, static generation, cache invalidation, and deployment checks.

The common failure mode is asking Claude Code to “connect Contentful” without defining the boundary. The result often works for one published English entry, then breaks when an editor previews a Japanese draft, publishes an updated image, or expects the production page to revalidate. Treat Claude Code as the agent that implements and reviews a defined CMS contract, not as a replacement for that contract.

For the broader CMS architecture, read Claude Code blog CMS. If the CMS data also feeds product APIs, connect it with Claude Code API development. For the monetization side of content operations, content funnel audit is the natural next step.

Choose the right Contentful API

Contentful has several APIs, and mixing them is the fastest way to leak secrets or show the wrong content. The Content Delivery API serves published content. The Content Preview API serves drafts and unpublished changes. The Content Management API manages models and entries. The Images API transforms assets. Keep those responsibilities separate in prompts, code, and environment variables.

APIWhat it is forTokenWhere to use it
Content Delivery APIPublished entries and assetsDelivery tokenSSG, ISR, server components, Astro builds
Content Preview APIDrafts and unpublished changesPreview tokenDraft mode and preview-only server routes
Content Management APIModels, entries, publish operationsManagement token or PATlocal scripts and trusted CI jobs
Images APIResizing and formatting assetssame context as delivery/previewimage helpers and thumbnails

The official Contentful JavaScript SDK documents the Preview API pattern: use the preview access token and host: "preview.contentful.com". Contentful also notes that Personal Access Tokens for the Management API are tied to a user and should be protected like credentials. Keep the official references close: Contentful JavaScript SDK, Contentful PAT guidance, Contentful environment permissions, and Astro’s Contentful guide.

Give Claude Code a concrete prompt

The prompt should define files, APIs, locales, and verification. A vague prompt produces vague architecture.

Implement a Contentful blog integration for this repository.
- Use Content Delivery API for published content only.
- Use Content Preview API only when preview mode is enabled.
- Use Content Management API only in scripts/setup-contentful-model.ts.
- Default locale is en-US; support ja-JP in function parameters.
- Next.js should use Draft Mode and revalidatePath for webhook invalidation.
- Astro should fetch Contentful entries at build time through getStaticPaths.
- Do not expose preview or management tokens to the browser.
- Show changed files and verification commands at the end.

This prompt makes review easier. If Claude Code puts CONTENTFUL_PREVIEW_TOKEN into a client component, the diff is obviously wrong. If it fetches drafts through cdn.contentful.com, the error is visible. If it forgets locale parameters, the contract catches it before production does.

Map the use cases before modeling fields

Contentful rewards structured modeling. A single rich text blob can ship quickly, but it becomes expensive when editors need previews, CTAs, localization, and analytics. Split the content shape by use case.

Use caseModel fieldsCache strategyBusiness outcome
Technical blogtitle, slug, excerpt, body, heroImage, tags, author, publishedAtSSG plus webhook revalidationnewsletter, free PDF, training leads
Product landing pageheadline, sections, pricingCopy, faq, ctaLabel, ctaUrlshorter ISR or manual revalidationpurchases and consultation
Documentationcategory, version, body, relatedDocs, updatedAtmostly SSG, search index syncpaid support and templates
Localized newslocalized title/body, region, canonicalSlugper-locale builds and previewregional CTAs and local teams

The mistake I made in a test project was putting blog posts and landing pages into the same blogPost type. It worked for the first demo, but the landing page needed a structured CTA, pricing copy, FAQ blocks, and experiment labels. Editors started embedding button copy inside rich text, which made analytics and design consistency harder. Claude Code can clean up this kind of model, but it is better to give it the right content shape first.

Keep secrets and locales explicit

Start with an .env.example. This is not busywork; it prevents token confusion.

CONTENTFUL_SPACE_ID=your_space_id
CONTENTFUL_ENVIRONMENT=master
CONTENTFUL_DEFAULT_LOCALE=en-US
CONTENTFUL_DELIVERY_TOKEN=delivery_token_for_published_content
CONTENTFUL_PREVIEW_TOKEN=preview_token_for_drafts
CONTENTFUL_MANAGEMENT_TOKEN=management_token_for_scripts
CONTENTFUL_PREVIEW_SECRET=random_secret_for_draft_mode
CONTENTFUL_REVALIDATE_SECRET=random_secret_for_webhooks
NEXT_PUBLIC_SITE_URL=http://localhost:3000

Do not put preview or management tokens behind NEXT_PUBLIC_. That prefix exposes values to the browser in Next.js. Delivery tokens are read-only, but for a server-rendered site there is usually no reason to expose even those. Also check the Contentful environment. Many examples use master, but production teams often use production, staging, or environment aliases. A token that can read master may still fail with 403 on another environment.

Locale bugs are just as common. If your URL is /ja/blog/foo but your query does not pass locale: "ja-JP", Contentful may return the default locale, fallback content, or nothing useful. Make locale a parameter in every fetch helper.

Build a typed fetch layer

Use one server-side client factory and switch the token and host only through an explicit preview flag. The code below is small enough for a real project and clear enough for Claude Code to review.

// src/lib/contentful.ts
import { createClient, type EntryFieldTypes, type EntrySkeletonType } from "contentful";

type BlogPostFields = {
  title: EntryFieldTypes.Symbol;
  slug: EntryFieldTypes.Symbol;
  excerpt: EntryFieldTypes.Text;
  body: EntryFieldTypes.RichText;
  heroImage: EntryFieldTypes.AssetLink;
  tags: EntryFieldTypes.Array<EntryFieldTypes.Symbol>;
  publishedAt: EntryFieldTypes.Date;
};

export type BlogPostSkeleton = EntrySkeletonType<BlogPostFields, "blogPost">;

function required(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`${name} is required`);
  return value;
}

export function getContentfulClient(options: { preview?: boolean } = {}) {
  const preview = options.preview ?? false;

  return createClient({
    space: required("CONTENTFUL_SPACE_ID"),
    environment: process.env.CONTENTFUL_ENVIRONMENT ?? "master",
    accessToken: preview
      ? required("CONTENTFUL_PREVIEW_TOKEN")
      : required("CONTENTFUL_DELIVERY_TOKEN"),
    host: preview ? "preview.contentful.com" : "cdn.contentful.com",
  });
}

export async function getBlogPosts(options: { locale?: string; preview?: boolean } = {}) {
  const locale = options.locale ?? process.env.CONTENTFUL_DEFAULT_LOCALE ?? "en-US";
  const response = await getContentfulClient(options).getEntries<BlogPostSkeleton>({
    content_type: "blogPost",
    order: ["-fields.publishedAt"],
    include: 2,
    locale,
  });

  return response.items;
}

export async function getBlogPostBySlug(
  slug: string,
  options: { locale?: string; preview?: boolean } = {},
) {
  const locale = options.locale ?? process.env.CONTENTFUL_DEFAULT_LOCALE ?? "en-US";
  const response = await getContentfulClient(options).getEntries<BlogPostSkeleton>({
    content_type: "blogPost",
    "fields.slug": slug,
    limit: 1,
    include: 2,
    locale,
  });

  return response.items[0] ?? null;
}

For larger models, generate types from the Contentful model and let CI catch drift. The important habit is the same: type the response shape and keep the API host selection in one place.

Wire Next.js preview and ISR carefully

In Next.js, published pages can be statically generated, preview pages can use Draft Mode, and Contentful webhooks can call a revalidation route. The current Next.js docs describe draftMode as an async function, and revalidatePath as a server-only cache invalidation function. Check the official pages for your version: draftMode and revalidatePath.

// app/api/revalidate/route.ts
import { revalidatePath } from "next/cache";
import type { NextRequest } from "next/server";

type ContentfulWebhookPayload = {
  fields?: {
    slug?: Record<string, string>;
  };
};

export async function POST(request: NextRequest) {
  const secret = request.headers.get("x-contentful-webhook-secret");
  if (secret !== process.env.CONTENTFUL_REVALIDATE_SECRET) {
    return Response.json({ revalidated: false }, { status: 401 });
  }

  const payload = (await request.json()) as ContentfulWebhookPayload;
  const slug = payload.fields?.slug?.["en-US"] ?? payload.fields?.slug?.["ja-JP"];

  revalidatePath("/blog");
  if (slug) revalidatePath(`/blog/${slug}`);

  return Response.json({ revalidated: true, slug: slug ?? null });
}

Configure the Contentful webhook for publish and unpublish events, not every save. Draft saves should be visible in preview, not in production cache invalidation. If the site still shows stale content, check whether the webhook hits the exact path, whether the route receives the custom secret header, and whether the entry slug is localized.

Use Astro for predictable static output

Astro is a strong fit when you want static pages and simple deployment. Fetch entries in getStaticPaths, pass the slug as props, and render rich text at build time. The official Astro guide uses Contentful’s SDK and typed entries, so Claude Code should not invent a custom REST client unless you need one.

For Astro, the operational question is rebuild strategy. A static build will not update just because Contentful changed. Use a Contentful webhook to trigger a platform rebuild, or keep high-frequency pages in Next.js where on-demand revalidation is available. This split is practical: long-lived docs and articles can be Astro SSG, while landing pages that change daily can use ISR.

Failure cases to review before shipping

The most expensive bugs are configuration bugs. Add these checks to Claude Code’s final review:

FailureSymptomFix
Preview token sent to Delivery host401 or no draft contentUse preview.contentful.com with the preview token
Delivery token used for draft modepreview page returns nullSwitch to the Content Preview API
Management token exposedsevere secret leakkeep CMA tokens in scripts and CI only
Locale omittedwrong language or empty fieldspass locale in every query
Asset not publishedentry renders but image is missingpublish/process the asset too
Webhook fires on saveproduction cache churns during editinguse publish/unpublish events
Field ID renamedcode and GraphQL schema breakpreserve API identifiers

Before publishing, run the app in a production-like mode. For Next.js, on-demand ISR behavior should be tested with a production build, not only next dev. For Astro, test the build output and the deployment rebuild hook. Ask Claude Code to list exactly which token, host, locale, and path each route uses.

Connect the CMS to monetization

A CMS integration should also support the business path. Add structured fields such as ctaKind, ctaLabel, ctaUrl, and relatedPosts instead of burying every CTA inside rich text. A technical tutorial can link to a free cheat sheet, a team adoption article can point to training and consultation, and template-heavy content can route to products.

That structure keeps analytics clean. You can track cta_click with a stable ID, compare CTAs by article type, and let editors update copy without changing React components. This is where Contentful and Claude Code work well together: Contentful stores the editorial structure, and Claude Code keeps the rendering, typing, and verification consistent.

After testing this setup, the biggest quality improvement came from separating Delivery, Preview, and Management responsibilities before writing code. Once that boundary was explicit, Claude Code produced smaller diffs, preview bugs were easier to catch, and locale issues became visible in review instead of after deployment.

#Claude Code #Contentful #CMS #headless CMS #content management
Free

Free PDF: Claude Code Cheatsheet

Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.

We handle your data with care and never send spam.

Level up your Claude Code workflow

Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.

Masa

About the Author

Masa

Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.