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.
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.
| API | What it is for | Token | Where to use it |
|---|---|---|---|
| Content Delivery API | Published entries and assets | Delivery token | SSG, ISR, server components, Astro builds |
| Content Preview API | Drafts and unpublished changes | Preview token | Draft mode and preview-only server routes |
| Content Management API | Models, entries, publish operations | Management token or PAT | local scripts and trusted CI jobs |
| Images API | Resizing and formatting assets | same context as delivery/preview | image 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 case | Model fields | Cache strategy | Business outcome |
|---|---|---|---|
| Technical blog | title, slug, excerpt, body, heroImage, tags, author, publishedAt | SSG plus webhook revalidation | newsletter, free PDF, training leads |
| Product landing page | headline, sections, pricingCopy, faq, ctaLabel, ctaUrl | shorter ISR or manual revalidation | purchases and consultation |
| Documentation | category, version, body, relatedDocs, updatedAt | mostly SSG, search index sync | paid support and templates |
| Localized news | localized title/body, region, canonicalSlug | per-locale builds and preview | regional 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:
| Failure | Symptom | Fix |
|---|---|---|
| Preview token sent to Delivery host | 401 or no draft content | Use preview.contentful.com with the preview token |
| Delivery token used for draft mode | preview page returns null | Switch to the Content Preview API |
| Management token exposed | severe secret leak | keep CMA tokens in scripts and CI only |
| Locale omitted | wrong language or empty fields | pass locale in every query |
| Asset not published | entry renders but image is missing | publish/process the asset too |
| Webhook fires on save | production cache churns during editing | use publish/unpublish events |
| Field ID renamed | code and GraphQL schema break | preserve 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.
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.
About the Author
Masa
Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.
Related Posts
Claude Code Obsidian to CLAUDE.md Workflow: Stop Re-explaining Context
Turn Obsidian working notes into concise CLAUDE.md operating notes that make Claude Code sessions easier to resume.
Claude Code Revenue CTA Routing: Send Articles to PDF, Gumroad, and Consultation
A Claude Code workflow for routing article readers to the free PDF, Gumroad products, or consultation by intent.
Claude Code Team Handoff Rules: Review Evidence, Permissions, Rollback, and Revenue Paths
A practical Claude Code handoff format for team review, proof, permission rules, rollback, free PDF, Gumroad, and consultation paths.
Related Products
50 Battle-Tested Claude Code Prompt Templates
Copy, paste, ship. 50 production-ready prompts.
Use proven prompts for code review, refactoring, testing, documentation, debugging, architecture, and incident response.
The Complete Claude Code Setup & Configuration Guide
From install to team-ready workflow.
A practical guide to installation, CLAUDE.md, hooks, MCP servers, permissions, IDE setup, and CI/CD workflows.