Skip to content

Configure the Storybook addon

To setup storybook-addon-vis, you need to do the following:

Also, if you are using TypeScript, you need to add the storybook-addon-vis/matcher type to your tsconfig.json.

To see working setups, open the published Storybooks and the testcases they are built from. For example, the CSF Next Storybook is built from sb-csf-next.

storybook-addon-vis provides sensible default setup for both Storybook and Vitest. So setting it up is pretty straightforward.

The basic setup will:

  • Use pixelmatch as the diffing algorithm.
  • Set config to compare image snapshot with a failure threshold of 0 pixels.
  • Timeout for image comparison is set to 30000 ms.
  • Local (non-CI) image snapshots are saved in the <root>/__vis__/local directory.
  • CI image snapshots are saved in the <root>/__vis__/<process.platform> directory.
  • Baseline images are saved in the <root>/__vis__/*/__baselines__ directory.
  • Image snapshots of the current test run are saved in the <root>/__vis__/*/__results__ directory.
  • Diff images are saved in the <root>/__vis__/*/__diffs__ directory.
  • You can review snapshots in the addon Vis panel. For a failed snapshot, compare the baseline with the result side by side, with a swipe divider, or as an onion skin, or view the diff image.

This setup assumes that you are writing your stories in CSF 3. If you are writing your stories in CSF Next, follow the CSF Next guide.

For Vitest, you need to do two things:

  • Add the storybookVis plugin in vitest.config.ts.
  • Enable visual testing in vitest.setup.ts.
vitest.config.ts
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'
import { storybookVis } from 'storybook-addon-vis/vitest-plugin'
import { defineConfig } from 'vitest/config'
export default defineConfig({
plugins: [
storybookTest(),
storybookVis() // ADD THIS
],
test: {
browser: { ... }, // your Vitest Browser Mode configuration
setupFiles: ['./vitest.setup.ts'], // ADD THIS
}
})
vitest.setup.ts
import { setProjectAnnotations } from '@storybook/your-framework'
import { vis, visAnnotations } from 'storybook-addon-vis/vitest-setup'
import { beforeAll } from 'vitest'
import * as projectAnnotations from './.storybook/preview.ts'
const annotations = setProjectAnnotations([
projectAnnotations,
visAnnotations
])
beforeAll(annotations.beforeAll)
// ADD THIS to enable visual testing
vis.setup()

Storybook 10.3+ and Vitest project annotations

Section titled “Storybook 10.3+ and Vitest project annotations”

From Storybook 10.3 onward, @storybook/addon-vitest adds a setup file to your Vitest project that calls setProjectAnnotations(getProjectAnnotations()). getProjectAnnotations() returns the annotations of your .storybook/preview.ts, so you do not need a hand-maintained vitest.setup.ts for them.

The addon skips this step when a setup file in your Storybook config folder (.storybook) contains the text setProjectAnnotations. It prints an info message when it does.

With the visAnnotations from storybook-addon-vis/vitest-setup, you have two ways to set up visual testing:

  1. Preview: merge visAnnotations into .storybook/preview.ts, as shown below. Keep only vis.setup() in vitest.setup.ts.
  2. Vitest setup: merge visAnnotations in setProjectAnnotations([...]) in vitest.setup.ts, as in Basic Setup. Because that file calls setProjectAnnotations, Storybook does not provision the preview annotations for you.

If you use CSF Next, use definePreview({ addons: [addonVis({ auto: true })] }) instead. It carries visAnnotations already. See the CSF Next guide.

.storybook/preview.ts
import type { Preview } from '@storybook/react-vite'
import { visAnnotations } from 'storybook-addon-vis/vitest-setup'
const preview: Preview = {
...visAnnotations,
tags: ['snapshot'],
}
export default preview
.storybook/vitest.setup.ts
import { vis } from 'storybook-addon-vis/vitest-setup'
vis.setup()

The testcase sb-csf-3 uses exactly this setup. The other testcases, for example sb-csf-mix, use the Vitest setup way.

You still need the storybookVis() Vitest plugin in both ways.

For Storybook, you need to add the addon to your .storybook/main.ts:

.storybook/main.ts
const config: StorybookConfig = {
addons: [
'storybook-addon-vis' // ADD THIS
]
}

In monorepo, you need to use the getAbsolutePath helper function to resolve the absolute path of the addon.

.storybook/main.ts
import type { StorybookConfig } from '@storybook/<your-framework>'
import { dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
/**
* This function is used to resolve the absolute path of a package.
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
*/
function getAbsolutePath(value: string): any {
return dirname(fileURLToPath(import.meta.resolve(`${value}/package.json`)))
}
const config: StorybookConfig = {
addons: [
getAbsolutePath('storybook-addon-vis') // ADD THIS
]
}
export default config

The getAbsolutePath function is a helper function typically added by Storybook automatically. The testcases in this repository, which live in a pnpm monorepo, use it in their .storybook/main.ts, for example sb-csf-3.

To take an automatic snapshot at the end of each story test, add the snapshot tag to your stories. You can add it to all stories in .storybook/preview.ts:

.storybook/preview.ts
export default {
tags: ['snapshot'],
}

With CSF Next, addonVis({ auto: true }) adds this tag for you. See the CSF Next guide.

Since storybook-addon-vis will save snapshot on your file system, you need to tell your version control system to ignore the snapshot directory.

With the basic setup, add this to your .gitignore (or your version control system’s ignore file):

.gitignore
**/__vis__/**/__diffs__
**/__vis__/**/__results__
**/__vis__/local

Note that the __vis__/*/__baselines__ should not be ignored, as it is used to store the baseline images. They should be committed to your repository.

If you want to do manual snapshot testing, you need to tell TypeScript about the matcher.

You can add the following to your tsconfig.json:

{
"compilerOptions": {
"types": ["storybook-addon-vis/matcher"]
}
}

Or use the triple-slash reference:

{
"includes": [..., "types"]
}
types/storybook-addon-vis.d.ts
/// <reference types="storybook-addon-vis/matcher" />

We recommend to set globals to false (which is the default). Setting globals to true actually works ok during test. But they don’t exist in the story files:

some.stories.tsx
export const Story = {
async play() {
// does not work
expect(true).toBeTruthy()
}
}

This is obvious because the story files are executed on the browser. In fact, you need to import the functions from @storybook/test instead:

some.test.ts
import { expect } from 'vitest'
// some.stories.ts
import { expect } from '@storybook/test'

Setting globals: true (and adding types: ["vitest/globals"] in your tsconfig.json) causes inconsistency and confuses both the editor and you.

If you need more control over the visual testing, you can customize the addon configuration.

In vitest.config.ts, you can customize where the snapshots are saved and how they are compared.

vitest.config.ts
import { storybookVis } from 'storybook-addon-vis/vitest-plugin'
import { defineConfig } from 'vitest/config'
export default defineConfig({
plugins: [
storybookVis({
snapshotRootDir: ({
ci, // true if running on CI
platform, // process.platform
providerName, // 'playwright' or 'webdriverio'
projectName, // name of the Vitest project
browserName,
screenshotFailures, // from `browser` config
screenshotDirectory, // from `browser` config
}) => `__vis__/${ci ? platform : 'local'}`,
// The default removes the common top folder (such as `src` or `tests`) from the test file path.
// Return `subpath` as is to keep the full path.
snapshotSubpath: ({ subpath }) => subpath,
// The default key in the file name of a snapshot that sets none, such as a manual snapshot.
// Automatic snapshots use the theme name (`auto` by default). Dash is not allowed.
snapshotKey: 'custom',
// set a default subject selector (e.g. `[data-testid="subject"]`) to capture image snapshot
subject: undefined,
comparisonMethod: 'pixel', // or 'ssim'
// pixelmatch or ssim.js options, depending on `comparisonMethod`.
diffOptions: undefined,
timeout: 30000,
failureThresholdType: 'pixel',
failureThreshold: 0,
})
]
})

The options are the same as the options in vitest-plugin-vis minus the preset.

For more details, please refer to the vitest-plugin-vis documentation.

In vitest.setup.ts, you can customize how the automatic snapshots are taken.

vitest.setup.ts
import { vis } from 'storybook-addon-vis/vitest-setup'
vis.setup({
// With automatic snapshot taking at the end of each test
auto: true,
// Control when automatic snapshot taking occurs
auto: async ({ tags }) => tags.includes('presentation'),
// Capture image snapshot at the end of each test for multiple themes (light and dark in this example).
//
// Note that this changes the theme in the `afterEach` hook.
// If you want to capture manual snapshots in different themes,
// configure Vitest to run the tests in different themes.
auto: {
async dark() { document.body.classList.add('dark') },
async light() { document.body.classList.remove('dark') },
}
})

For several Vitest projects or custom snapshot paths, see Monorepos and custom snapshot paths. To create the first baselines, see Get started.