TurboSnap config paths
TurboSnap’s path options don’t all resolve against the same base. Some are relative to your repository root, others to the current working directory. Mixing them up is the most common reason TurboSnap looks for your Storybook in the wrong place — especially in a monorepo, where workingDir moves the goalposts for some options but not others.
For what each option does, see the configuration options reference. To set up TurboSnap in the first place, see Setup.
Which base does each option use
Relative to the repository root:
untracedexternalsstorybookBaseDir
Relative to the current working directory:
storybookConfigDirstorybookBuildDir
workingDir changes the current working directory, so it affects the second group and leaves the first group alone.
ℹ️ storybookBaseDir defaults to the current working directory expressed relative to the repository root. If you set it explicitly, your value is read as repo-root-relative — not as an addition to workingDir.
Why storybookBaseDir exists
storybookBaseDir corrects a mismatch between two sets of file paths:
- the paths inside
preview-stats.json, which your Storybook build generates - the paths returned by
git diff --name-only, which TurboSnap runs
If your Storybook build and the chromatic command run from the same working directory, the two line up, and you don’t need storybookBaseDir at all. If they run from different directories, the paths won’t match, and TurboSnap can’t connect a changed file to the stories that depend on it.
One exception: if your build-storybook script changes directory before invoking storybook build, the paths can diverge even when Chromatic builds your Storybook for you.
✨ Run npx @chromatic-com/turbosnap-helper from your repository root, and it will report the correct base and config directories for your project, and can update your config file for you.
Worked example
Given a Storybook in client/, with these directories:
📙 Storybook Base Directory: ./client
📂 Storybook Config Directory: ./client/.storybook
A working GitHub Actions step looks like this:
- name: Build Storybook
run: npm run build-storybook -- -o ./client/storybook-static -c ./client/.storybook
- name: Publish to Chromatic
uses: chromaui/action@latest
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
workingDir: client
onlyChanged: true
# 👇 relative to workingDir, so no ./client prefix
storybookBuildDir: storybook-static
externals: |
./client/public/*.css
**/*.sass
untraced: |
**/preview.js
# 👇 relative to the repository root, so ./client is required
./client/src/utils.tsx
Notes on each option in that config:
storybookBuildDiris cwd-relative, andworkingDirhas already moved the cwd toclient, so it’sstorybook-staticrather than./client/storybook-static.storybookConfigDiris omitted entirely. It defaults to.storybook, which is correct once the cwd isclient.storybookBaseDiris omitted. Its default is the cwd relative to the repository root, which is./client— already right.untracedandexternalsare repo-root-relative and are unaffected byworkingDir, so they need the./clientprefix. A pattern like./src/utils.tsxwould silently match nothing.
For more on monorepo setups, see Using TurboSnap in a monorepo and Optimizing TurboSnap for monorepos.
Common failures
The config directory path is duplicated (foo/bar/foo/bar/.storybook)
You’ve set storybookConfigDir to a repo-root-relative path while also using workingDir. Because storybookConfigDir is cwd-relative, Chromatic appends your value to the working directory.
# ✖ Chromatic looks for web/app/web/app/sb-config/.storybook
workingDir: web/app
storybookConfigDir: web/app/sb-config/.storybook
# ✔ relative to the working directory
workingDir: web/app
storybookConfigDir: sb-config/.storybookIf your config directory is at the default location inside your working directory, drop storybookConfigDir altogether.
The base directory resolves to the repository root instead of a subdirectory
You’ve set workingDir in CI but storybookBaseDir somewhere else — in your chromatic script or chromatic.config.json — with a path relative to the subproject rather than the repository.
// ✖ Chromatic looks for ./app/sb at the repository root
{ "storybookBaseDir": "./app/sb" }Because storybookBaseDir is repo-root-relative, workingDir: web/services does not make this resolve to ./web/services/app/sb. Either give the full repo-root-relative path, or set it in the Chromatic step of your CI workflow where the full path is natural:
storybookBaseDir: web/services/app/sbFlags take precedence over config-file options, and you can supply both in the same build. Check every place a path might be set: your CI configuration, your package.json scripts, and chromatic.config.json.
I see “No manifest or lockfile found at the root of the repository”
TurboSnap is looking for your project’s manifest and lockfile in the wrong place, which usually means your Storybook configuration lives in a subdirectory, and the base directory isn’t set correctly.
Run npx @chromatic-com/turbosnap-helper from your repository root to get the correct value. If you still can’t determine the right path, generate a trimmed stats file and use the trace utility to see which paths TurboSnap resolves.