Skip to content

Migrating from Chromatic

View as Markdown

This guide walks through migrating an existing Chromatic project to StoryShelf. It assumes you have a Chromatic project with existing baselines and CI integration.

Coming from Lost Pixel? See the Lost Pixel migration guide.

  • A running StoryShelf server (see Getting started)
  • Admin access to your Chromatic project
  • Ability to modify CI pipelines
  • GitHub/GitLab admin for status check updates

1. Export baselines from Chromatic (manual — no API)

Section titled “1. Export baselines from Chromatic (manual — no API)”

Chromatic does not provide a public API to export baselines. You cannot programmatically migrate baseline images.

Workaround: On first StoryShelf run, all snapshots will appear as “new” or “changed” (against default branch). Your team will need to re-accept them once. This is a one-time cost.

Tip: If you have a small number of critical baselines, you can download them from the Chromatic UI and package them as a baseline archive to import. This isn’t practical for large projects.

StoryShelf doesn’t repeat this lock-in: once you’re on it, baseline export/import moves your baselines to any other instance or into backups.

Follow Getting started to deploy:

  • Choose database (SQLite for single-node, Turso/Postgres for serverless)
  • Choose storage (local for single-node, S3-compatible for multi-node)
  • Choose auth (OIDC recommended for teams)
  • Configure Git provider (GitHub/GitLab) for status checks

In StoryShelf UI or via CLI:

Terminal window
storyshelf create --url https://shelf.example.com --name "My Design System" --token $ADMIN_TOKEN

Match Chromatic settings where applicable:

  • Project name → same as Chromatic
  • Default branch → same as Chromatic (usually main)
  • Pixel threshold → match Chromatic’s threshold if customized
  • Viewports → configure in server ShelfConfig.viewports to match Chromatic
- name: Chromatic
uses: chromatic-action@v1
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
buildScriptName: build-storybook
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # affected capture diffs against history
- name: Build Storybook
run: npx build-storybook -o storybook-static
- name: Upload to StoryShelf
run: npx storyshelf upload --token ${{ secrets.STORYSHELF_TOKEN }} --sha ${{ github.sha }} --branch ${{ github.ref_name }}
env:
STORYSHELF_URL: https://shelf.example.com
STORYSHELF_SLUG: my-design-system

Or with .storybook/storyshelf.json committed:

- name: Upload to StoryShelf
run: npx storyshelf
env:
STORYSHELF_TOKEN: ${{ secrets.STORYSHELF_TOKEN }}

StoryShelf reads both chromatic: and storyshelf: parameter keys. storyshelf: wins on conflict.

Chromatic parameterStoryShelf equivalentNotes
disableSnapshotdisableSnapshotWorks unchanged
flakyTestflakyTestWorks unchanged
delaydelayWorks unchanged
diffThresholddiffThresholdWorks unchanged
pauseAnimationAtEndpauseAnimationAtEndWorks unchanged
viewportsServer configSet in ShelfConfig.viewports
modes / globalsNot supportedGap — no equivalent

No code changes needed for existing chromatic: parameters. They work as-is.

ChromaticStoryShelf
Global baseline + branch baselinesPer-branch baselines with default-branch fallback
Branch baselines independentFeature branch accepts → branch baseline; merge to main → promotes to default
No GC (manual cleanup)Auto GC: branchTtlDays (default 30), purgeTtlDays (default 30)

Implication: On first run, feature branches will diff against main baseline. After accepting changes, they get their own branch baseline. Merging to main updates the default baseline automatically.

FeatureChromaticStoryShelfImpact
TurboSnapYes (dependency graph)Yes — affected capture (on by default)Similar skip rates on Vite Storybooks; needs fetch-depth: 0 and preview-stats.json
Cross-browserChrome, FF, Safari, Edge (branded, cloud)Chromium / Firefox / WebKit, one per buildSet in project Settings → General or via API; baselines tracked per browser; no branded Safari/Edge
Modes/GlobalsYesNoTheme/locale matrices need workarounds
A11y testingYes (aXe)axe-core checks, on by default (runA11y)Results are shown in review, not a review gate
Cloud parallelizationAutomaticManual (testing.concurrency)Limited by your server CPU/memory
Baseline exportUI onlyAPI + UICannot bulk-import Chromatic baselines
Flake detectionAuto-detectExplicit flakyTestManual marking required
  • Zero per-snapshot cost — Unlimited snapshots forever
  • Full data ownership — Your DB, your storage, your network
  • No vendor lock-in — MIT license, standard REST API, open source
  • Flexible SSO — Any OIDC provider (Keycloak, Okta, Entra ID, etc.)
  • Deploy anywhere — Node, Bun, Deno, Fly.io, Vercel, Cloudflare, Lambda, etc.
  • Per-branch baselines with fallback — Cleaner for long-lived feature branches
  • Persistent builds — Tag releases → never purged

Keep Chromatic running in parallel for 1-2 sprints:

  1. Run both — Add StoryShelf upload as additional CI step (don’t remove Chromatic yet)
  2. Compare results — Verify StoryShelf catches same regressions
  3. Team training — Familiarize reviewers with StoryShelf UI
  4. Cutover — Remove Chromatic step, update GitHub status check requirements
  5. Archive — Export any critical Chromatic data before canceling subscription
  • ☐ Reviewers know how to approve/reject in StoryShelf UI
  • ☐ GitHub status check renamed from chromatic to storyshelf/testing (or your channel prefix)
  • ☐ Branch protection rules updated to require StoryShelf status
  • ☐ CI secrets rotated (CHROMATIC_PROJECT_TOKEN → STORYSHELF_TOKEN)
  • ☐ Documentation updated with StoryShelf workflow
  • ☐ Chromatic subscription canceled (after verification period)

Q: Can I import Chromatic baselines? A: No public API. Baselines must be re-accepted on first StoryShelf run.

Q: Does StoryShelf support TurboSnap? A: Yes, as affected capture — enabled by default with the same shape (ancestor → git diff → graph trace → selective render). Differences: Vite stats only, no per-snapshot billing (skipping saves CPU, not money), and unknown cases fail open to full renders. Replace --only-changed with nothing (it’s the default); use --full to opt out.

Q: Can I test Safari/Firefox? A: Yes — set the project’s browsers list (for example ["chromium", "firefox", "webkit"], up to 3) via PATCH /api/v1/projects/:slug, or the single capture browser in Settings → General. Every build captures each listed engine in turn, with one baseline per engine; a newly added engine is seeded from its first build, auto-accepting stories whose primary-browser snapshot is clean.

Q: What about Modes/Globals? A: Not supported. Workaround: duplicate stories with different args, or use separate Storybook configs.

Q: How do I handle flaky tests? A: Add parameters: { storyshelf: { flakyTest: true } } or tag flaky-test on the story. Build status stays green with warning.

Q: Can I run both during migration? A: Yes. Add StoryShelf as additional CI step. Compare results before cutover.