How to Integrate Uniform with Your Headless CMS
Connect Uniform to structured content so your team can ship personalized pages, A/B tests, and campaign variants without copying content between tools.
What is Uniform?
Uniform is a composable digital experience platform for building personalized, testable digital experiences across websites, apps, and commerce journeys. Teams use Uniform Canvas for visual composition and Uniform Context for signals, intents, audiences, personalization, and experimentation. It sits between content, commerce, analytics, and front-end frameworks so marketers can assemble experiences while developers keep control of the implementation.
Why integrate Uniform with a headless CMS?
Personalization gets messy when your page content lives in one place and your campaign rules live somewhere else. A marketer changes a hero headline for returning visitors, but the variant in Uniform still points to last week's copy. Or an experiment ends, and someone has to manually update the source entry, the landing page, and the test configuration before the site reflects the winner.
Connecting Uniform to a headless CMS category tool solves that split by letting Uniform use structured content as the source for page sections, campaign entries, product stories, and localized variants. With Sanity's AI Content Operating System, content is typed JSON in the Content Lake, so Uniform doesn't need to scrape rendered HTML or parse large page blobs. GROQ can fetch exactly the fields a Uniform component needs, such as headline, CTA label, image asset, audience key, experiment ID, and referenced product data.
The alternative is usually brittle. Teams copy content into Uniform parameters by hand, schedule batch exports, or wait for a nightly job before a new audience variant appears. Real-time webhooks and Functions make the flow event-based instead. When a Sanity document is published, updated, or deleted, the integration can call Uniform right away, update the matching composition or component data, and let the front end render the right experience for each visitor.
Architecture overview
A typical Sanity and Uniform integration starts with structured campaign, landing page, or component documents in Sanity's Content Lake. When an editor publishes a document, a Sanity webhook can fire with a GROQ filter such as _type in ["landingPage", "campaign", "personalizedHero"] && !(_id in path("drafts.**")). The webhook sends the document ID and mutation metadata to a Sanity Function or a small HTTP endpoint. The Function then uses @sanity/client to fetch the full published document with GROQ, including referenced fields Uniform needs for personalization and experimentation. For example, it can join an audience reference, resolve an image URL, and project only the fields used by a Uniform Canvas component. That keeps the payload small and predictable. Next, the Function maps the Sanity document to Uniform's model. Depending on your setup, it can create or update a Uniform Canvas composition, update component parameters that reference Sanity entries, or attach audience and test metadata that Uniform Context uses at runtime. The server-side code calls Uniform's Canvas API through @uniformdev/canvas with the Uniform project ID and API key. On the front end, your Next.js, Nuxt, or React app loads the Uniform composition, resolves the Sanity-backed fields, reads the Uniform Context manifest, and renders the variant that matches the visitor's signals, intent, or experiment assignment.
Common use cases
Audience-based hero variants
Publish one Sanity landing page with multiple structured hero variants, then let Uniform choose the right headline, image, and CTA for new visitors, returning customers, or account-based segments.
A/B test content without duplicate pages
Keep the test copy and assets in Sanity, map each variant to a Uniform experiment, and avoid cloning entire pages just to test one module.
Localized campaign personalization
Use Sanity references for market, language, and region-specific content, then let Uniform combine locale rules with visitor signals on the front end.
Commerce journey targeting
Sync Sanity product story content into Uniform compositions so shoppers can see different feature blocks based on category interest, cart context, or campaign source.
Step-by-step integration
- 1
Set up Uniform project access
Create a Uniform project, define the component types you want editors to compose in Canvas, and copy the project ID and API key from Uniform settings. Install the front-end SDKs you need, commonly @uniformdev/canvas, @uniformdev/context, and @uniformdev/context-react.
- 2
Model personalized content in Sanity Studio
Define schemas for the content Uniform will consume. A personalized hero might include title, slug, audience reference, experimentKey, variantKey, headline, image, ctaLabel, ctaUrl, and locale. Keep these as typed fields instead of embedding HTML so Uniform receives clean data.
- 3
Create the event trigger
Add a Sanity webhook for publish, update, and delete events, or create a Sanity Function if you want the sync logic to run inside Sanity's server-side runtime. Use a GROQ filter so only relevant document types trigger the integration.
- 4
Fetch the exact payload with GROQ
In the handler, use @sanity/client to fetch the published document by ID. Project references and assets into the shape Uniform needs, such as audience key, experiment ID, image URL, and component fields.
- 5
Write to Uniform
Use Uniform's Canvas API through @uniformdev/canvas to create or update the matching composition, component parameters, or Sanity entry reference. Store Uniform IDs back on the Sanity document if you need a stable one-to-one mapping.
- 6
Test the front-end experience
Run the site locally with Uniform Context tools enabled, preview audience signals and experiment assignments, publish a Sanity change, and confirm that the Uniform-rendered page updates without a manual copy step.
Code example
import { createClient } from '@sanity/client';
import { CanvasClient } from '@uniformdev/canvas';
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: '2025-01-01',
token: process.env.SANITY_READ_TOKEN!,
useCdn: false,
});
const uniform = new CanvasClient({
projectId: process.env.UNIFORM_PROJECT_ID!,
apiKey: process.env.UNIFORM_API_KEY!,
});
export async function POST(req: Request) {
const body = await req.json();
const id = String(body._id).replace(/^drafts\./, '');
const page = await sanity.fetch(`*[_id == $id][0]{
title,
"slug": slug.current,
"audience": audience->key,
experimentKey,
hero{headline, ctaLabel, ctaUrl, "imageUrl": image.asset->url}
}`, { id });
if (!page?.slug) return Response.json({ skipped: true });
const composition = {
type: 'landingPage',
_name: page.title,
_slug: page.slug,
parameters: {
title: { type: 'text', value: page.title },
audience: { type: 'text', value: page.audience },
experimentKey: { type: 'text', value: page.experimentKey }
},
slots: {
hero: [{
type: 'hero',
parameters: {
headline: { type: 'text', value: page.hero?.headline },
image: { type: 'image', value: page.hero?.imageUrl },
ctaText: { type: 'text', value: page.hero?.ctaLabel },
ctaUrl: { type: 'link', value: page.hero?.ctaUrl }
}
}]
}
};
await uniform.upsertComposition({ composition });
return Response.json({ synced: page.slug });
}How Sanity + Uniform works
Build your Uniform integration on Sanity
Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect Uniform personalization and experimentation to the rest of your content operation.
Start building free โCMS approaches to Uniform
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Structured data for personalization | Often stores page content as templates or HTML blocks, which makes it harder to send clean audience-specific fields to Uniform. | Structures content as typed JSON in the Content Lake, with references that GROQ can join into the exact shape Uniform needs. |
| Real-time sync on publish | May rely on scheduled exports, plugins, or custom scripts that update personalization data later. | Uses webhooks and Functions to trigger Uniform sync logic on content mutations without polling or a separate worker. |
| Experiment variant modeling | Variants are often duplicated as full pages, which creates cleanup work when a test ends. | Lets developers define schemas for experiments, audiences, variants, and reusable modules, then customize Sanity Studio around that workflow. |
| Field-level query control | Integrations may receive too much rendered page data, then filter it after the fact. | GROQ can filter, project, join references, sort, and slice in one query before sending data to Uniform. |
| Multi-channel reuse | Content is often tied to website pages, so mobile, Uniform, and AI agent use cases need copies or adapters. | One structured back end can feed web, mobile, Uniform, Agent Context, and other systems from the same content model. |
Keep building
Explore related integrations to complete your content stack.
Sanity + LaunchDarkly
Connect structured Sanity content with feature flags so teams can release content-backed experiences to selected users or accounts.
Sanity + Optimizely
Use Sanity content as the source for experiment copy, assets, and page modules while Optimizely handles testing and decisioning.
Sanity + Dynamic Yield
Send structured product, campaign, and editorial content from Sanity into Dynamic Yield personalization programs.