How to Integrate Chromatic with Your Headless CMS
Connect Chromatic to your headless CMS so content changes trigger Storybook visual tests and catch layout regressions before they ship.
What is Chromatic?
Chromatic is a cloud service for Storybook that runs visual regression tests, publishes Storybooks, and adds UI review to pull requests. It was created by the Storybook team and is used by frontend teams that ship component libraries, design systems, and product UI across many routes, states, and breakpoints.
Why integrate Chromatic with a headless CMS?
Your components can pass every unit test and still break when real content lands. A 22-character CTA becomes 48 characters in German, a product card gets a missing image, or an editorial team publishes a campaign title that wraps into three lines on mobile. Chromatic catches those UI changes by comparing rendered Storybook snapshots, but it only sees what your stories render.
Connecting Chromatic to a headless CMS lets you test components against current content, not stale mock data from last quarter. For developer tools teams, that means Storybook stories can use real docs, release notes, product cards, navigation labels, localization strings, and campaign content. When Sanity sends a publish webhook, your integration can fetch exactly the fields needed from the Content Lake with GROQ, update a Storybook fixture, build Storybook, and run Chromatic.
Thereβs a trade-off. Chromatic doesnβt ingest content records directly. It tests rendered UI, so you need a small bridge that turns structured content into stories, fixtures, or preview routes. The alternative is manual QA, screenshots in Slack, or waiting until production analytics tells you a page is broken.
Architecture overview
A typical setup starts with a Sanity webhook that fires when a document is published, updated, or deleted. Use a GROQ-powered webhook filter such as _type in ["landingPage", "product", "releaseNote"] so Chromatic doesnβt run for unrelated edits. The webhook sends the document ID to a small handler or a Sanity Function. The handler verifies the request, fetches the latest document from the Content Lake with @sanity/client, and uses GROQ to select only the fields Storybook needs, for example title, hero image URL, CTA label, slug, locale, and referenced theme tokens. That data is written to a JSON fixture or passed to a CI job. Chromatic is usually called from CI with its CLI, not by pushing content records to a content API. The workflow builds Storybook, then runs npx chromatic with your CHROMATIC_PROJECT_TOKEN. The Chromatic CLI uploads the Storybook build and snapshots to Chromatic, where reviewers see visual diffs in the Chromatic app and PR checks. A Sanity Function can route and filter the event without external infrastructure, but a full Storybook build usually belongs in GitHub Actions, GitLab CI, CircleCI, or another build runner.
Common use cases
Content-driven visual regression
Run Chromatic when Sanity content changes so components are tested with current headlines, images, CTAs, and references.
Localization overflow checks
Snapshot translated content in Storybook to catch text wrapping, clipped buttons, and broken card grids before release.
Design system QA with real examples
Feed Storybook stories with structured Sanity fixtures so design system reviewers see components in realistic content states.
Campaign release review
Test upcoming launch pages, banners, and product modules in Chromatic before a scheduled publish goes live.
Step-by-step integration
- 1
Set up Chromatic
Create a Chromatic project, connect your Git provider, copy the project token, and install the CLI with npm install --save-dev chromatic. Add a CI command such as npx chromatic --project-token=$CHROMATIC_PROJECT_TOKEN.
- 2
Model the content in Sanity Studio
Define schema fields that map to rendered components, for example title, dek, heroImage, ctaLabel, locale, slug, and references to product, theme, or author documents.
- 3
Create Storybook stories that read content fixtures
Build stories for the components Chromatic should snapshot, then load Sanity-derived JSON fixtures instead of hard-coded lorem ipsum.
- 4
Create the Sanity webhook or Function
Add a publish webhook with a GROQ filter for the document types that affect UI. Send a small payload, usually _id and _type, to your listener or Function.
- 5
Fetch Sanity content and run Chromatic
Use @sanity/client and GROQ to fetch the current document, write the fields to a Storybook fixture, build Storybook, and call the Chromatic CLI with your project token.
- 6
Test the review loop
Publish a safe test document in Sanity, confirm that the webhook starts the workflow, review the Chromatic diff, and decide whether visual changes should block the PR or only notify the team.
Code example
import express from "express";
import {createClient} from "@sanity/client";
import {mkdir, writeFile} from "node:fs/promises";
import {execFile} from "node:child_process";
import {promisify} from "node:util";
const exec = promisify(execFile);
const app = express();
app.use(express.json());
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: "production",
apiVersion: "2025-02-19",
token: process.env.SANITY_READ_TOKEN,
useCdn: false,
});
const query = `*[_id == $id][0]{
_id,
_type,
title,
dek,
ctaLabel,
"slug": slug.current,
"imageUrl": heroImage.asset->url,
"theme": theme->name
}`;
app.post("/sanity/chromatic", async (req, res) => {
const doc = await sanity.fetch(query, {id: req.body._id});
if (!doc) return res.status(202).send("No document to snapshot");
await mkdir(".storybook/fixtures", {recursive: true});
await writeFile(
".storybook/fixtures/sanity-current.json",
JSON.stringify(doc, null, 2)
);
await exec("npm", ["run", "build-storybook"]);
await exec("npx", [
"chromatic",
"--project-token",
process.env.CHROMATIC_PROJECT_TOKEN!,
"--storybook-build-dir",
"storybook-static"
]);
res.status(202).send("Chromatic build uploaded");
});
app.listen(3000);How Sanity + Chromatic works
Build your Chromatic integration on Sanity
Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect Chromatic to the content changes that affect your UI.
Start building free βCMS approaches to Chromatic
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Content shape for visual tests | Content often renders as pages or HTML blocks, so teams copy examples into Storybook by hand. | Typed JSON in the Content Lake can be queried with GROQ and shaped into Storybook-ready fixtures in one request. |
| Triggering Chromatic on publish | Visual checks usually happen during code deploys, not when editors change live content. | GROQ-powered webhooks and Functions can filter content events, route them, and start a Chromatic workflow. |
| Field-level control | Templates decide what data is available, which makes small fixture payloads hard to produce. | GROQ projects only the fields Chromatic needs, including joined references, image URLs, and localized values. |
| Testing content variants | Teams often rely on screenshots, preview links, or manual checks for long copy and missing media. | Schema-as-code keeps content fields versioned with the app, and Storybook fixtures can be generated from the same schema. |
| Where the work runs | The safest path is usually a scheduled job or manual QA because publish events are tied to the platform. | Functions can handle event logic without external infrastructure, while CI runs the full Storybook build for Chromatic. |
| Setup trade-off | Initial setup can be familiar, but visual testing stays disconnected from content changes. | You need to define schemas, webhooks, and fixtures, then Chromatic can test real content changes consistently. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Storybook
Build component stories with real Sanity fixtures, then send those stories to Chromatic for visual review.
Sanity + GitHub
Start GitHub Actions from Sanity publish events so content changes can run Storybook builds and Chromatic checks.
Sanity + Linear
Turn content-driven visual regressions into tracked Linear issues with document links, owners, and release context.