How to Integrate Cloudflare Pages with Your Headless CMS
Trigger Cloudflare Pages builds the moment structured content changes, so your static site, edge-rendered pages, and preview routes stay current without manual rebuilds.
What is Cloudflare Pages?
Cloudflare Pages is Cloudflare's platform for deploying frontend applications and static sites to its global network. Teams use it with frameworks like Astro, Next.js, Nuxt, Remix, Gatsby, SvelteKit, and Vue, with builds connected to Git and optional server-side logic through Pages Functions. It's a common choice when you want fast deploys, edge delivery, branch previews, and built-in Cloudflare services in the same workflow.
Why integrate Cloudflare Pages with a headless CMS?
Cloudflare Pages can ship a new build every time code changes, but content changes are a different problem. If an editor publishes a pricing page at 2:00 PM, you don't want someone to manually click βRetry deployment,β commit a JSON file, or wait for a nightly batch job. Connecting Cloudflare Pages to a headless CMS category tool solves that gap by turning publish events into deploy events.
Architecture overview
A typical setup starts in Sanity Studio, where editors publish structured documents such as pages, posts, products, or navigation items. That content is held as typed JSON in the Content Lake. When a publish event happens, a Sanity webhook or Function runs with a filter such as _type in ["page", "post"] && !(_id in path("drafts.**")). The handler uses GROQ to fetch the exact fields Cloudflare Pages needs, including referenced data like author names, category slugs, or navigation labels. Then it calls Cloudflare Pages through a deploy hook URL or the Cloudflare Pages API, usually POST /client/v4/accounts/{account_id}/pages/projects/{project_name}/deployments for a Git-connected project. Cloudflare Pages starts a new build, your framework fetches fresh content from Sanity during npm run build, the output is deployed to Cloudflare's edge network, and visitors receive the updated page from the nearest Cloudflare location. If you need server-side processing before the deploy, use Sanity Functions to run the logic on content events without running a separate webhook server.
Common use cases
Publish-to-deploy websites
Trigger a Cloudflare Pages production build when an editor publishes a page, post, or landing page in Sanity Studio.
Branch previews for content QA
Send draft or release content to Cloudflare Pages preview deployments so editors can review campaign pages before launch.
Static navigation and route generation
Use GROQ to fetch slugs, menus, and redirects at build time, then let Cloudflare Pages serve generated routes from the edge.
Product content on fast storefronts
Publish product descriptions, editorial collections, and buying guides from Sanity while Cloudflare Pages serves the storefront frontend.
Step-by-step integration
- 1
Create the Cloudflare Pages project
In Cloudflare, create a Pages project from GitHub or GitLab, choose your framework preset, set the build command such as npm run build, and set the output directory such as dist, build, or .output/public. Add environment variables for SANITY_PROJECT_ID, SANITY_DATASET, and SANITY_API_VERSION. If you'll trigger deploys through the API, create a Cloudflare API token with Account, Cloudflare Pages, Edit permission. If you prefer the simpler path, create a Pages deploy hook under Settings, Builds & deployments, Deploy hooks.
- 2
Install the SDKs your integration needs
In your webhook service, Sanity Function, or build tooling, install the Sanity client and Cloudflare SDK with npm i @sanity/client cloudflare. In the Cloudflare Pages frontend app, use @sanity/client to fetch content during build time, or fetch from your own API route if you need extra permission checks.
- 3
Model the content in Sanity Studio
Define schemas for the content Cloudflare Pages will render. A page schema usually needs title, slug, seoTitle, seoDescription, body, publishedAt, and references such as author or parentPage. Keep route-critical fields typed and required, especially slug.current, so your build can generate stable paths.
- 4
Create the publish trigger
Create a Sanity webhook or Function that runs only for published content. Use a filter like _type in ["page", "post", "product"] && !(_id in path("drafts.**")). Include the document _id, _type, and slug in the projection so the handler can decide whether to trigger a production deploy, preview deploy, or no deploy.
- 5
Call Cloudflare Pages
For a Git-connected Pages project, call the Cloudflare Pages deployment API or POST to the deploy hook URL. The deploy hook is easier to maintain because Cloudflare owns the secret URL. The API token path gives you more control if you need to inspect projects, choose branches, or build internal tooling around deployments.
- 6
Test the frontend build
Publish a test document in Sanity Studio, confirm the webhook fires, confirm Cloudflare Pages starts a deployment, and inspect the build logs. Then verify that your frontend GROQ query returns the expected fields and that missing optional fields don't break the build.
Code example
import Cloudflare from "cloudflare";
import {createClient} from "@sanity/client";
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: "2025-02-19",
useCdn: false,
token: process.env.SANITY_READ_TOKEN
});
const cloudflare = new Cloudflare({
apiToken: process.env.CLOUDFLARE_API_TOKEN!
});
export async function POST(req: Request) {
const event = await req.json();
const page = await sanity.fetch(
`*[_id == $id][0]{_id, _type, "slug": slug.current, title}`,
{id: event._id}
);
if (!page?.slug) {
return Response.json({skipped: true, reason: "No routable slug"});
}
const deployment = await cloudflare.pages.projects.deployments.create(
process.env.CLOUDFLARE_PAGES_PROJECT!,
{
account_id: process.env.CLOUDFLARE_ACCOUNT_ID!,
branch: "main"
}
);
return Response.json({
triggered: true,
slug: page.slug,
deploymentId: deployment.id
});
}How Sanity + Cloudflare Pages works
Build your Cloudflare Pages integration on Sanity
Sanity gives you the structured content foundation, real-time event system, GROQ queries, and flexible APIs you need to connect Cloudflare Pages to your publishing workflow.
Start building free βCMS approaches to Cloudflare Pages
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Build triggers on publish | Often depends on manual exports, plugins, or scheduled jobs before Cloudflare Pages can rebuild. | Webhooks and Functions can trigger Cloudflare Pages only for the publish events you choose, such as pages, posts, or products. |
| Build-time data shape | Cloudflare Pages may receive rendered HTML or large page blobs that are hard to reuse across routes. | GROQ returns the exact JSON shape your build needs, including joins across references like author, category, and navigation. |
| Schema changes | Field changes can live in admin settings, which makes code review and rollback harder. | Schema-as-code in Sanity Studio keeps route fields, SEO fields, and content rules in the same review flow as your frontend. |
| Preview workflow | Preview often points back to the publishing system instead of your Cloudflare Pages frontend. | Sanity can send draft or release content to preview routes while Cloudflare Pages handles branch and preview deployments. |
| Scaling beyond the website | Content is often tied to page templates, so reuse in apps or AI agents requires cleanup work. | Structured content in the Content Lake can feed Cloudflare Pages, mobile apps, localization workflows, and AI agents from the same source. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Next.js
Build dynamic routes, preview flows, and static pages with structured content from Sanity.
Sanity + Astro
Ship content-heavy static sites that fetch typed JSON from the Content Lake at build time.
Sanity + Remix
Connect Sanity content to nested routes, loaders, and server-rendered experiences.