Personalization & Experimentation8 min read

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.

Published April 29, 2026
01 โ€” Overview

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.


02 โ€” The case for integration

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.


03 โ€” Architecture

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.


04 โ€” Use cases

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.


05 โ€” Implementation

Step-by-step integration

  1. 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. 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. 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. 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. 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. 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.


06 โ€” Code

Code example

typescriptapi/sanity-to-uniform.ts
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 });
}

07 โ€” Why Sanity

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 โ†’

08 โ€” Comparison

CMS approaches to Uniform

CapabilityTraditional CMSSanity
Structured data for personalizationOften 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 publishMay 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 modelingVariants 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 controlIntegrations 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 reuseContent 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.

09 โ€” Next steps

Keep building

Explore related integrations to complete your content stack.

Ready to try Sanity?

See how Sanity's Content Operating System powers integrations with Uniform and 200+ other tools.