How to Integrate Storybook with Your Headless CMS
Connect Storybook to structured content so designers, engineers, and reviewers can test UI states with real copy, images, and reference data before code ships.
What is Storybook?
Storybook is an open-source workshop for building, testing, and documenting UI components in isolation. Frontend teams use it with React, Vue, Angular, Svelte, Web Components, and other frameworks to create stories that show specific component states. It’s a common tool in design systems, component libraries, and product teams that need repeatable UI review outside the full app.
Why integrate Storybook with a headless CMS?
Component stories are only useful if they reflect the content your UI actually handles. A hero component with "Lorem ipsum" won’t catch a 92-character headline, a missing image alt value, or a referenced author object that changes the layout. When Storybook reads structured content from a headless CMS category tool, your stories can cover real editorial states instead of hand-written mock data that drifts after two sprints.
Architecture overview
A typical integration starts when an editor publishes or updates content in Sanity Studio. A Sanity webhook fires on the matching mutation, often filtered to types like landingPage, hero, productCard, or testimonial. The webhook calls a Next.js route, serverless function, CI endpoint, or Sanity Function. That handler uses @sanity/client and GROQ to fetch the full shape Storybook needs, including references, image metadata, and only published fields. The handler then converts the result into Storybook-friendly args, usually JSON files under src/stories/__fixtures__ or a small internal endpoint consumed by Storybook loaders. If your Storybook is published through CI or Chromatic, the same handler can call GitHub, GitLab, or a build webhook to rebuild the static Storybook. End users then open Storybook and see components rendered with current structured content, not stale mocks.
Common use cases
Real-content component states
Render Button, Hero, Card, and Navigation stories with published Sanity content so long labels, missing images, and nested references are tested early.
Design system documentation
Show approved editorial examples beside design tokens and component props, giving designers and engineers one place to review usage.
Content-driven visual review
Trigger a Storybook or Chromatic rebuild after a publish event so reviewers can catch layout regressions caused by real content changes.
Fixture generation for tests
Generate stable JSON fixtures from GROQ queries and reuse them in Storybook, unit tests, and visual snapshots.
Step-by-step integration
- 1
Set up Storybook
In your frontend repo, run npx storybook@latest init and choose the framework builder that matches your app, such as @storybook/react-vite or @storybook/nextjs. Storybook itself doesn’t require an account or API key. If you publish visual reviews with Chromatic, create a Chromatic project token and add it to CI.
- 2
Install Sanity and helper packages
Add @sanity/client to the repo or to the webhook service that will prepare Storybook data. If you commit generated fixtures back to GitHub, add @octokit/rest. Keep SANITY_PROJECT_ID, SANITY_DATASET, SANITY_API_TOKEN, and any CI or Git provider tokens in environment variables.
- 3
Model Storybook-ready content in Sanity Studio
Create schemas that match real UI inputs, not page blobs. For a Hero story, fields might include eyebrow, title, subtitle, image, image.alt, cta.label, cta.href, and theme. Add validation for required component inputs so Storybook doesn’t hide bad content with fallback strings.
- 4
Create a publish-triggered sync
Add a Sanity webhook filtered to publish events and relevant document types. Point it at a webhook listener, Sanity Function, or middleware route. In the handler, fetch the canonical document with GROQ, expand references, and map the result to Storybook args.
- 5
Connect the data to Storybook
Use Storybook args, loaders, or imported JSON fixtures. For stable visual tests, generated fixtures are usually better than live fetches because every screenshot uses the same input. For internal preview stories, loaders can read directly from a local API endpoint.
- 6
Test the frontend experience
Run npm run storybook locally, verify the content-driven stories, and add at least three edge cases, such as a long headline, no CTA, and a portrait image. Then run npm run build-storybook in CI and, if you use Chromatic, publish the build for visual review.
Code example
A minimal Next.js webhook route that receives a Sanity publish event, fetches content with GROQ, and updates a JSON fixture consumed by a Storybook story. Storybook has no content-ingest API, so this pushes data into the Storybook project instead.
import {createClient} from '@sanity/client';
import {Octokit} from '@octokit/rest';
import type {NextRequest} from 'next/server';
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: '2025-02-19',
token: process.env.SANITY_API_TOKEN!,
useCdn: false,
});
const github = new Octokit({auth: process.env.GITHUB_TOKEN!});
export async function POST(req: NextRequest) {
const {_id} = await req.json();
const hero = await sanity.fetch(`
*[_id == $id][0]{
title,
subtitle,
"imageUrl": image.asset->url,
"imageAlt": image.alt,
cta{label, href}
}
`, {id: _id.replace('drafts.', '')});
const path = 'src/stories/__fixtures__/hero.json';
const repo = {owner: 'acme', repo: 'web'};
const current = await github.repos.getContent({...repo, path});
await github.repos.createOrUpdateFileContents({
...repo,
path,
message: `Update Storybook hero fixture from ${_id}`,
content: Buffer.from(JSON.stringify(hero, null, 2)).toString('base64'),
sha: Array.isArray(current.data) ? undefined : current.data.sha,
});
return Response.json({ok: true});
}
// Hero.stories.tsx
import type {Meta, StoryObj} from '@storybook/react';
import hero from './__fixtures__/hero.json';
import {Hero} from '../components/Hero';
export default {component: Hero} satisfies Meta<typeof Hero>;
export const Published: StoryObj<typeof Hero> = {args: hero};How Sanity + Storybook works
Build your Storybook integration on Sanity
Sanity’s AI Content Operating System gives you structured content, real-time publish events, GROQ, and flexible APIs for connecting Storybook to the same source your products use.
Start building free →CMS approaches to Storybook
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Component-ready data shape | Often outputs page HTML or template-specific fields, so teams create separate mock data for Storybook. | Content Lake stores structured JSON, and GROQ can return the exact args shape a Storybook component expects. |
| Publish-triggered updates | Storybook updates usually depend on manual exports, scheduled jobs, or someone copying content into fixtures. | Webhooks can filter publish events with GROQ, while Functions can run sync logic without extra infrastructure. |
| Reference handling | Authors, categories, media, and related entries are often tied to templates, which makes isolated component testing harder. | GROQ can join references, project image URLs, and flatten nested fields into one fixture for Storybook. |
| Visual testing stability | Live page content can change outside the test flow, causing noisy screenshots. | You can generate pinned fixtures on publish and reuse them in Storybook, unit tests, and Chromatic runs. |
| Schema changes | Field definitions may live in an admin UI, making review and version control harder. | Sanity Studio uses schema-as-code, so component prop changes and content schema changes can be reviewed in the same pull request. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Chromatic
Run visual reviews when Sanity content changes generate new Storybook fixtures or builds.
Sanity + GitHub
Commit generated Storybook fixtures, trigger Actions workflows, and review content-driven UI changes in pull requests.
Sanity + Linear
Create product and design tasks when content changes expose missing component states or visual regressions.