Configure the Storybook addon
To setup storybook-addon-vis, you need to do the following:
- Add the
storybookVisplugin in Vitest along with thestorybookTestplugin. - Configure visual testing in
vitest.setup.tsand/or.storybook/preview.ts(see Storybook 10.3+ and Vitest project annotations; with CSF Next, prefer preview-only setup as in the CSF Next guide). - Add storybook-addon-vis addon to your Storybook configuration in
.storybook/main.ts. - Ignore snapshot folders in your
.gitignorefile.
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.
Basic Setup
Section titled “Basic Setup”storybook-addon-vis provides sensible default setup for both Storybook and Vitest. So setting it up is pretty straightforward.
The basic setup will:
- Use
pixelmatchas 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__/localdirectory. - 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
Vispanel. 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
storybookVisplugin invitest.config.ts. - Enable visual testing in
vitest.setup.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 }})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 testingvis.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:
- Preview: merge
visAnnotationsinto.storybook/preview.ts, as shown below. Keep onlyvis.setup()invitest.setup.ts. - Vitest setup: merge
visAnnotationsinsetProjectAnnotations([...])invitest.setup.ts, as in Basic Setup. Because that file callssetProjectAnnotations, 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.
import type { Preview } from '@storybook/react-vite'import { visAnnotations } from 'storybook-addon-vis/vitest-setup'
const preview: Preview = { ...visAnnotations, tags: ['snapshot'],}
export default previewimport { 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:
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.
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 configThe 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:
export default { tags: ['snapshot'],}With CSF Next, addonVis({ auto: true }) adds this tag for you. See the CSF Next guide.
Ignore snapshot folders
Section titled “Ignore snapshot folders”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):
**/__vis__/**/__diffs__**/__vis__/**/__results__**/__vis__/localNote that the __vis__/*/__baselines__ should not be ignored,
as it is used to store the baseline images.
They should be committed to your repository.
TypeScript Configuration
Section titled “TypeScript Configuration”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"]}/// <reference types="storybook-addon-vis/matcher" />Disable Vitest global API
Section titled “Disable Vitest global API”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:
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:
import { expect } from 'vitest'
// some.stories.tsimport { 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.
Advanced Setup
Section titled “Advanced Setup”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.
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.
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.