How to Integrate Remix with Your Headless CMS
Connect Remix to structured content so loaders, actions, and resource routes can render fresh pages, update cached data on publish, and serve the same content to web, mobile, and AI agents.
What is Remix?
Remix is a full-stack React framework built around web fundamentals like server-rendered routes, nested layouts, loaders, actions, and standard HTTP responses. Teams use Remix to build fast web apps, ecommerce storefronts, dashboards, documentation sites, and content-heavy experiences where data loading and user interactions live close to the route.
Why integrate Remix with a headless CMS?
Remix is strongest when each route can ask for exactly the data it needs. A product detail page might need product copy, price notes, related articles, and SEO fields. A help center route might need category navigation, article body content, and locale fallbacks. If that content lives in spreadsheets, hardcoded JSON files, or page-shaped blobs, every change turns into developer work or fragile parsing.
Architecture overview
A typical Remix integration starts with structured content in Sanity's Content Lake. Editors publish a document in Sanity Studio, such as a post, product page, landing page, or help article. A Sanity webhook fires on the publish mutation and sends the document ID, type, and dataset to a Remix resource route, for example POST /api/sanity-webhook. That Remix action verifies a shared secret, uses @sanity/client, and runs a GROQ query to fetch the exact fields the app needs. From there, the action can update Redis, write to a search index, purge a platform cache, or call another Remix resource route that warms route data. If you don't want to host the sync logic inside Remix, a Sanity Function can run the same server-side workflow on content mutations and call your Remix endpoint without another server. When an end user requests /blog/remix-content, the Remix loader reads from Sanity directly or from the refreshed cache, returns JSON through the route, and React renders the page.
Common use cases
Server-rendered editorial sites
Use Remix loaders to fetch article, author, image, and SEO fields from Sanity before the route renders.
Product storytelling pages
Pair commerce data with Sanity product copy, buying guides, FAQs, and merchandising content in the same Remix route.
Localized content routes
Query locale-specific fields with GROQ, then let Remix nested routes render language-aware navigation and page content.
Publish-triggered cache updates
Use Sanity webhooks or Functions to refresh Remix route data the moment a page, post, or product story is published.
Step-by-step integration
- 1
Create the Remix app
Run npx create-remix@latest, choose your deployment target, then install the Sanity client with npm install @sanity/client. Remix doesn't require an account or framework API key. You'll use Sanity project credentials instead.
- 2
Add Sanity environment variables
Set SANITY_PROJECT_ID, SANITY_DATASET, SANITY_READ_TOKEN if your dataset is private, SANITY_WEBHOOK_SECRET, and REMIX_ORIGIN. Keep write tokens out of browser code. Remix loaders and actions run on the server, which is where these values belong.
- 3
Model content in Sanity Studio
Create schemas for the content your Remix routes need. For a blog route, that usually means title, slug, excerpt, mainImage, author reference, publishedAt, body as Portable Text, and SEO fields. For product pages, add productId, merchandising copy, related guides, and FAQ arrays.
- 4
Fetch content in Remix loaders
Create a server-only Sanity client and call it from route loaders. Use GROQ projections to return only the fields the route needs, such as slug.current, image asset metadata, and referenced author names.
- 5
Create a publish sync path
Add a Remix resource route like /api/sanity-webhook, then configure a Sanity webhook to call it on create, update, and delete events for the document types you render. If you prefer to keep sync logic inside Sanity, use a Sanity Function triggered by content mutations.
- 6
Test the frontend path
Publish a draft in Sanity Studio, confirm the webhook reaches Remix with a 200 response, verify the GROQ query returns the expected fields, and load the affected Remix route in production mode with npm run build && npm start.
Code example
import { json, type ActionFunctionArgs } from "@remix-run/node";
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
});
export async function action({ request }: ActionFunctionArgs) {
if (request.headers.get("x-sanity-secret") !== process.env.SANITY_WEBHOOK_SECRET) {
return json({ error: "Unauthorized" }, { status: 401 });
}
const { _id, _type } = await request.json();
if (_type !== "post") return json({ ok: true, skipped: true });
const post = await sanity.fetch(
`*[_id == $id][0]{_id,title,"slug":slug.current,excerpt,"author":author->{name}}`,
{ id: _id.replace(/^drafts\./, "") }
);
if (!post) return json({ ok: true, deleted: true });
await fetch(`${process.env.REMIX_ORIGIN}/resources/cache`, {
method: "POST",
headers: { "content-type": "application/json", "x-cache-secret": String(process.env.CACHE_SECRET) },
body: JSON.stringify({ key: `post:${post.slug}`, value: post })
});
return json({ ok: true, slug: post.slug });
}How Sanity + Remix works
Build your Remix integration on Sanity
Sanity gives your Remix app a structured content foundation, real-time event system, and flexible APIs for route loaders, resource routes, cache updates, and AI content workflows.
Start building free →CMS approaches to Remix
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Remix route data | Pages often arrive as rendered HTML, which makes Remix loaders parse or proxy more than they need. | GROQ can return route-shaped JSON with references joined in the query, so a loader can fetch one compact payload. |
| Publish-to-site freshness | Content changes often depend on a rebuild, manual export, or plugin-specific cache behavior. | Webhooks call Remix actions on publish, and Functions can run mutation-triggered sync logic without separate infrastructure. |
| Schema control | Content fields are often configured in an admin UI, which can drift from the Remix codebase. | Schema-as-code keeps content types in JavaScript or TypeScript, reviewed in the same workflow as Remix route changes. |
| Preview and editing workflow | Editors may preview inside the publishing tool, but it may not match the Remix app's real routing and layouts. | Sanity Studio and the Presentation Tool can connect draft content to Remix routes, with Content Source Maps tracing rendered fields back to their source. |
| Multi-channel reuse | Content is often page-first, so reuse across Remix, mobile, and AI agents takes extra mapping. | Structured content in the Content Lake can serve Remix, mobile, search, and production AI agents through APIs and Agent Context. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Next.js
Build React apps with Sanity content, draft previews, route-based rendering, and cache updates through Next.js.
Sanity + Astro
Ship content-heavy static and server-rendered sites with Astro components backed by structured Sanity data.
Sanity + Vercel
Deploy Sanity-backed web apps on Vercel, then use webhooks to trigger cache updates and production previews.