ScreenshotNeo

BlogHow-to

How to Configure Chromatic Viewports for Responsive Screenshots

Configure responsive Storybook snapshots with Chromatic Modes, choose viewport sizes, control cropping, and troubleshoot common runner-specific issues.

By the ScreenshotNeo team4 October 20267 min read

For a Storybook project, configure responsive Chromatic screenshots with the Modes API. Define viewport dimensions in .storybook/modes.ts, then attach the modes to the stories or components whose responsive behavior you want to review. Each mode creates a separate snapshot with its own baseline and approval.

Use Storybook viewport presets when your project already has them, and set parameters.chromatic.cropToViewport only when you want the screenshot clipped to the configured viewport height. Chromatic’s default is 1200 × 900 when no viewport is configured.

1. Define responsive modes

Create a shared mode map. The dimensions below are CSS pixels:

// .storybook/modes.ts
export const allModes = {
  mobile: { viewport: { width: 375, height: 812 } },
  desktop: { viewport: { width: 1280, height: 900 } },
} as const;

Apply the modes to a story or component through its Storybook parameters:

// Example.stories.ts
import type { Meta, StoryObj } from '@storybook/react';
import { allModes } from '../.storybook/modes';
import { Example } from './Example';

const meta = {
  component: Example,
  parameters: {
    chromatic: {
      modes: {
        mobile: allModes.mobile,
        desktop: allModes.desktop,
      },
    },
  },
} satisfies Meta<typeof Example>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {};

Adjust the import paths and component type for your project. The mode definitions can be reused, while each story can select only the viewports relevant to that component.

Choose the right scope

  • Story scope: Apply modes to a single story when only that state has responsive behavior worth checking.
  • Component scope: Put modes in the component’s metadata when its stories should share responsive snapshots.
  • Project scope: Set modes globally only when the extra snapshots and approvals are useful across the project. Global modes can multiply snapshot review work.

Chromatic documents modes for Storybook 6.3 and later. If your setup predates that, check the current compatibility guidance before migrating.

2. Reuse named Storybook viewport presets

If the project already defines named viewport presets in .storybook/preview.ts, you can reference their keys from Chromatic modes. This keeps Storybook’s viewport selector and Chromatic capture settings aligned.

// .storybook/preview.ts
import type { Preview } from '@storybook/react';

const preview: Preview = {
  parameters: {
    viewport: {
      options: {
        phone: {
          name: 'Phone',
          styles: { width: '375px', height: '812px' },
          type: 'mobile',
        },
        wide: {
          name: 'Wide desktop',
          styles: { width: '1280px', height: '900px' },
          type: 'desktop',
        },
      },
    },
  },
};

export default preview;
// .storybook/modes.ts
export const allModes = {
  phone: { viewport: 'phone' },
  wide: { viewport: 'wide' },
} as const;

Then use allModes.phone and allModes.wide in the story’s chromatic.modes map as shown above. A preset’s width and height should be whole-pixel values with px units. Chromatic mode dimensions do not accept values such as rem or calc(), even though Storybook’s viewport feature may support them elsewhere.

3. Set dimensions and understand screenshot bounds

Chromatic Modes accepts a viewport as an integer width, an object containing integer width and/or height, or an integer string with an optional px suffix. The documented dimension range is 200–2560 pixels, and each snapshot is limited to 25,000,000 pixels.

Configuration Behavior
No viewport Chromatic’s documented default is 1200 × 900.
Width only Uses that width and trims the capture to the content height.
Height only Uses a default width of 1200 pixels and trims to the content width.
Width and height Sets the capture browser dimensions. The screenshot still captures the rendered UI’s full height by default.
cropToViewport: true Clips the snapshot to the configured viewport. Content taller than the viewport can be cut off; content shorter than the viewport is captured only to its intrinsic height.

To capture only the visible viewport rather than the page’s full rendered height, set the crop parameter on the story:

const meta = {
  component: Example,
  parameters: {
    chromatic: {
      modes: {
        mobile: allModes.mobile,
      },
      cropToViewport: true,
    },
  },
};

Use cropping deliberately. A full-height capture is useful for checking content flow, while a clipped capture matches a fixed browser viewport. If you need both behaviors, create distinct stories or configurations so reviewers can tell which result they are approving.

Large captures and device pixel ratio

Very tall or wide captures can hit browser image limits. Chromatic documents a 32,767-image-pixel dimension limit in Safari and Firefox. At device pixel ratio 2, that corresponds to 16,383.5 CSS pixels; Chromatic says it automatically retries affected captures at DPR 1.0. The 25-million-pixel snapshot limit still applies, so keep enormous pages and high-density captures in mind when choosing dimensions.

4. Use viewport settings with other test runners

The Modes configuration above is for Storybook. When Chromatic captures tests from another runner, configure that runner’s browser viewport instead.

Vitest

Set the viewport in the browser configuration in vitest.config.ts, or set it for an individual test using the page API supported by your Vitest browser setup:

// In a browser test
await page.viewport(375, 812);

Exact configuration placement depends on the Vitest browser provider and version in use. Keep the viewport declaration in the same runner configuration that Chromatic executes.

Playwright

Set a project-wide viewport in Playwright configuration:

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    viewport: { width: 375, height: 812 },
  },
});

Or set it for a specific test:

import { test } from '@playwright/test';

test.use({ viewport: { width: 375, height: 812 } });

test('renders at phone width', async ({ page }) => {
  await page.goto('/');
});

Cypress

Configure the viewport dimensions globally in Cypress configuration or set them in a test with Cypress’s supported test configuration. Chromatic explicitly documents that cy.viewport() is unsupported for Chromatic capture, so do not rely on that call to set the capture viewport.

// cypress.config.ts
import { defineConfig } from 'cypress';

export default defineConfig({
  viewportWidth: 375,
  viewportHeight: 812,
});

Runner APIs and configuration evolve. Refer to the official Chromatic viewport guide for the current runner-specific instructions.

5. Migrate from the legacy viewports parameter

The older Storybook setting is parameters.chromatic.viewports, an array of widths. Chromatic describes it as a legacy API replaced by Modes and plans to deprecate it. Modes allow explicit height and combinations of global settings. Chromatic converts legacy entries during capture, but the viewports and modes APIs cannot be used together.

// Legacy; do not combine with chromatic.modes
parameters: {
  chromatic: {
    viewports: [375, 1280],
  },
}

For new configurations, use chromatic.modes. When migrating, replace each width entry with a named mode, decide whether a fixed height is meaningful, and remove the legacy parameter from that story. See the legacy viewport reference and Modes viewport guide.

6. Understand viewport precedence

Storybook viewport globals can affect the canvas and may be respected by Chromatic. A story-level chromatic.viewport parameter or a mode that sets a viewport takes precedence. Chromatic also ignores non-pixel viewport globals. Storybook can assign a viewport through globals.viewport.value, but for predictable Chromatic output prefer an explicit mode on the story being tested.

7. Troubleshoot common problems

Symptom Likely cause Fix
The capture uses the default size The mode was defined but not attached under parameters.chromatic.modes, or a referenced preset key is misspelled. Check the story’s effective parameters, verify the mode name and preset key, and confirm the mode map is imported from the right path.
Only one viewport appears The story has only one mode attached, or the project is still using the legacy width array. Attach each desired mode explicitly. Migrate to Modes and do not configure both APIs on the same story.
The screenshot is taller than the configured height Chromatic captures the full rendered UI height by default. Set chromatic.cropToViewport: true when clipping is the intended result.
The bottom of the UI is missing Cropping is enabled, so content beyond the viewport is clipped. Remove cropping for a full-height snapshot, or increase the viewport height.
A dimension is rejected or behaves unexpectedly The value is outside 200–2560 pixels, is not a whole-pixel dimension, or uses unsupported units such as rem or calc(). Use an integer or integer pixel string in the supported range.
A huge page fails to capture The snapshot may exceed 25 million pixels or a browser dimension limit. Reduce width, height, or device pixel ratio; split the content into focused stories where practical.
A Cypress capture ignores cy.viewport() Chromatic documents this command as unsupported for capture. Set viewportWidth and viewportHeight in Cypress configuration or supported test configuration.
Viewport setting in Storybook differs from Chromatic Storybook globals may be overridden by a story-level Chromatic viewport or a mode; non-pixel globals are ignored. Put the intended dimensions in the Chromatic mode and remove conflicting settings.

8. Keep responsive snapshots useful

  1. Choose widths that exercise real layout changes. Include widths around breakpoints used by the component rather than every possible width.
  2. Use targeted modes. Every additional mode creates a separately reviewed snapshot and baseline, so apply them where responsive behavior matters.
  3. Decide whether height is part of the check. Set an explicit height for fixed viewport comparisons; otherwise let full-height capture show the whole rendered UI.
  4. Keep mode names stable. Names such as mobile and desktop make snapshot differences understandable across reviews.
  5. Watch capture size. Large dimensions and high DPR can increase image size and run time, and may hit documented browser or snapshot limits.

Or skip the browser setup

If you need screenshots of live websites at chosen viewport sizes rather than Storybook component baselines, ScreenshotNeo provides a screenshot API. Its API accepts viewport settings and returns an image or PDF. See the ScreenshotNeo API docs for parameters and formats.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -d viewport_width=375 \
  -d viewport_height=812 \
  -o shot.webp

Cookie banners, newsletter popups and chat widgets are removed before the shot. Bot checks, blank pages and failed loads are never billed. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up for 1,000 free screenshots a month, with no card required.

Frequently asked questions

Does a Chromatic mode create another baseline?

Yes. Each applied mode creates a separate snapshot with its own baseline and approval.

Can I set only a viewport height?

Yes. Chromatic documents a default width of 1200 pixels when only height is set, then trims the capture to the content width.

Should modes be applied to every story?

Usually apply them only where responsive behavior is meaningful. Project-wide modes create more snapshots to review and approve.

Can I use the legacy viewport array with Modes?

No. Chromatic documents that viewports and modes cannot be used simultaneously on a story.

Sources