Configure viewports for stories
ℹ️ This page documents viewports using the modes API. Learn how to get started. If you are transitioning from the chromatic.viewports
API to the modes API, please consult the migration guide.
Define viewport modes
Modes are defined in the .storybook/modes.js
file. If your project doesn’t have this file yet, go ahead and create it. To set viewport in a mode, specify the screen width and/or height using the chromatic[<your-mode-name>].viewport
parameter.
The following are all acceptable viewport values:
- Integer (which defaults to width)
- Width & height (integer values only)
- String that is integer or string integer with
px
suffix, e.g.:'1000px'
- Only width (snapshot will be trimmed to the content height)
- Only height (snapshot will use the default width of 1200px, and be trimmed to the content width)
// .storybook/modes.js
export const allModes = {
default: {
// integer is just width
viewport: 1280,
},
specificBoth: {
// object can specify both
viewport: {
height: 300,
width: 800,
},
},
specificWidth: {
// object with width
viewport: {
width: 800,
},
},
specificHeight: {
// object with height
viewport: {
height: 800,
},
},
specificString: {
// string values
viewport: {
height: "600",
width: "800px",
},
},
};
Apply modes to set viewports
Modes can be applied at different levels: project, component, or story. When a mode includes a valid viewport parameter, Chromatic will adjust the viewport size to match the defined dimensions while capturing the snapshot.
For example, given the following set of modes in .storybook/modes.js
.
// .storybook/modes.js
export const allModes = {
small: { name: "Small", styles: { width: "640px", height: "900px" } },
medium: { name: "Medium", styles: { width: "768px", height: "900px" } },
large: { name: "Large", styles: { width: "1024px", height: "900px" } },
};
We can apply the modes, like so:
// ArticleCard.stories.js
import { allModes } from "../.storybook/modes";
import { ArticleCard } from "./ArticleCard";
export default {
component: ArticleCard,
title: "ArticleCard",
parameters: {
chromatic: {
//🔶 Test each story for ArticleCard in two modes
modes: {
mobile: allModes["small"],
desktop: allModes["large"],
},
},
},
};
export const Base = {
args: {
//...
},
};
export const MembersOnly = {
args: {
//...
},
};
When Chromatic captures your story, it will create two snapshots on your build, with the browser set at each viewport. These modes will be treated separately, with independent baselines and distinct approvals.
Combining modes with viewports addon
The Storybook viewport addon enables you to adjust the dimensions of the story canvas. Developers use it to verify the responsive behavior of components when building UIs. With modes, you can easily reference the different viewport sizes that you have configured for the addon.
Reference viewport by name
You start by configuring your desired set of viewports in .storybook/preview.js
. For example:
⚠️ While the viewport addon allows you to specify dimensions using any valid CSS unit (such as px, rem, calc, etc.), Chromatic modes only support whole numbers or strings with a “px” suffix.
// .storybook/preview.js
const preview = {
parameters: {
viewport: {
viewports: {
xsm: { name: "XSmall", styles: { width: "320px", height: "900px" } },
sm: { name: "Small", styles: { width: "640px", height: "900px" } },
md: { name: "Medium", styles: { width: "768px", height: "900px" } },
lg: { name: "Large", styles: { width: "1024px", height: "900px" } },
xl: { name: "XL", styles: { width: "1280px", height: "900px" } },
"2xl": { name: "2XL", styles: { width: "1536px", height: "900px" } },
},
},
},
};
export default preview;
You can now refer to these viewports by their key in your modes definition. For example:
// .storybook/modes.js
export const allModes = {
xsm: {
viewport: "xs",
},
md: {
viewport: "md",
},
xl: {
viewport: "xl",
},
// Note, you can still specify the more
// specific options listed in the section above
specific: {
viewport: {
height: 300,
width: 800,
},
},
};
Using defaultViewport
Within Storybook, you have the ability to configure the default viewport for stories at different levels: project, component, or story. This can be done by setting the parameters.viewport
value. By adjusting this setting, you can control the dimensions of the story canvas when viewing it in the browser using Storybook. Chromatic will respect the defaultViewport
setting when capturing snapshots.
defaultViewport
can ve set via a named viewport from the viewport addon:
// .storybook/preview.js
const preview = {
viewport: {
viewports: {
small: {
name: "small",
styles: {
width: "375px",
height: "375px",
},
},
},
},
};
// MyComponent.stories.js
export const DefaultViewportFromName = Template.bind({});
export const DefaultViewportFromName = {
args: {
//...
},
parameters: {
viewport: {
defaultViewport: "small",
},
},
};
That said, there are a few exceptions when Chromatic will ignore the defaultViewport
setting:
- If your story has a
chromatic.viewport
parameter. - If your story uses a mode that specifies viewport.
defaultViewport
is set to a non-pixel value, eg:50%
,65em
,calc(100vh - calc(70% + 2w))
, etc.
In the example below, both stories will use the md
viewport size when viewed in the browser. However, FirstStory
will have one snapshot captured using the md
viewport size, while SecondStory
will have two snapshots captured at lg
and xl
viewport sizes respectively.
// MyComponent.stories.jsx
import type { Meta, StoryObj } from '@storybook/react';
import { allModes } from '../.storybook/modes';
import { MyComponent } from './MyComponent';
const meta: Meta<typeof MyComponent> = {
component: MyComponent,
title: 'MyComponent',
};
export default meta;
type Story = StoryObj<typeof MyComponent>;
export const FirstStory: Story = {
parameters: {
viewport: {
defaultViewport: 'md',
},
},
};
export const SecondStory: Story = {
parameters: {
viewport: {
defaultViewport: 'md',
},
chromatic: {
modes: {
lg: allModes['lg'],
xl: allModes['xl'],
},
},
},
};
Migration from viewports (legacy) to modes
The new modes API is a successor to the viewport feature and takes it a step further. With Modes, you can test your stories in different viewports and any combination of global settings you define. Additionally, you can specify specific viewport heights for tests.
Currently, we will continue to support both APIs, but our plan is to deprecate the viewport feature. Behind the scenes, Chromatic will automatically convert viewports to modes when capturing a snapshot. If you are currently using the viewports feature, now is a good time to migrate to the new modes API.
Can I use Viewports and Modes simultaneously?
No, Chromatic will throw an error if you use both. Additionally, if you include the “viewports” key, Chromatic will convert each entry in the array into a separate mode.
For example, the following will be converted into two modes: 320px
and 1200px
parameters: {
chromatic: { viewports: [320, 1200] },
}
Frequently asked questions
Are there any constraints on the viewport size that I can choose?
A width or height can be any whole number between 200 and 2560 pixels. The maximum number of pixels per snapshot is 25,000,000.
Can I control the height of the viewport?
Yes, you can control the height using the viewport.height
property and enabling parameters.chromatic.cropToViewport
.
- If intrinsic height of the root container is greater than the specified
viewport.height
then the snapshot will be clipped to viewport.height. - If intrinsic height of the root container is less than the specified
viewport.height
then the snapshot will be trimmed to that intrinsic height.
If no height is specified, Chromatic will capture a snapshot based on the intrinsic height of the root container.
// MyComponent.stories.js
import { MyComponent } from "./MyComponent";
export default {
component: MyComponent,
title: "MyComponent",
parameters: {
chromatic: {
cropToViewport: true,
modes: {
small: {
viewport: {
height: 300,
width: 800,
},
},
},
},
},
};
How do I assign viewports globally to all components in my Storybook?
We don’t recommend this in most cases because each viewport is treated independently and snapshots must be approved as such.
But if you really want to assign project level modes, you can do so by setting the chromatic.modes
parameter in .storybook/preview.js
:
// .storybook/preview.js
import { allModes } from "../.storybook/modes";
const preview = {
parameters: {
chromatic: {
modes: {
light: allModes["light"],
dark: allModes["dark"],
},
},
},
};
export default preview;
What happens when I don’t specify a viewport?
Chromatic defaults to a viewport of width 1200px and height 900px.
How does snapshot cropping work with viewport width and height?
When you add a viewport, Chromatic will size the browser’s viewport to the defined width and height. It will then take a snapshot and crop it to the bounding box of the component. This eliminates negative space around snapshots, reducing the visual information you must review.
By default, Chromatic captures the full height of the rendered UI, even if a viewport height has been set. This is because in most cases, you want to capture the entire rendered UI. To restrict the capture height to the specified height, set parameters.chromatic.cropToViewport
to true
.