Storybook Default Args with Placeholder Image URLs
Set storybook placeholder image URLs as default args for image props so every story renders correctly without requiring real assets or mocked fetch calls.
Storybook stories with image props often show broken-image icons in the default state because no `src` value is provided. Setting a deterministic storybook placeholder image URL as the default arg means every story renders a visually complete component on first open — with correct dimensions and no network dependency on real assets.
This guide covers setting component-level and story-level default args, creating a shared placeholder constants file, integrating with Chromatic for visual regression, and avoiding the common mistake of using random URLs that produce non-deterministic snapshots.
Problem
Why Storybook stories break without image default args
A `ProductCard` component with an `imageSrc` prop renders a broken icon if the story does not provide a value. Designers reviewing the story in Storybook see an incomplete component, and Chromatic's visual regression baseline captures the broken state — meaning any future fix triggers a false positive diff.
Using a real production image URL as the default arg causes a different problem: the URL may change, return a 404, or be unavailable in CI networks. Developers end up committing local image paths that only work on their machine, or skipping the image prop altogether.
A deterministic placeholder URL solves both. The URL is always valid, returns the same SVG for the same parameters, and can be calculated from the component's expected dimensions — making the default arg self-documenting.
Component story
Setting placeholder default args at the component level
Set image defaults at the component (`Meta`) level so every story in the file inherits them. Override at the story level only when you are specifically testing a different image state — a tall image, a landscape image, a missing image, etc.
For components with multiple image props (e.g. a user card with `avatarSrc` and `coverSrc`), provide defaults for all of them. A partially-rendered component is harder to review than a fully-rendered one.
// ProductCard.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { ProductCard } from './ProductCard';
import { ph } from '../../.storybook/placeholders';
const meta: Meta<typeof ProductCard> = {
component: ProductCard,
args: {
imageSrc: ph.card,
title: 'Running Shoes',
price: '$89.00',
},
};
export default meta;
type Story = StoryObj<typeof ProductCard>;
export const Default: Story = {};
export const LargeImage: Story = {
args: { imageSrc: ph.cardLarge },
};
export const BrokenImage: Story = {
args: { imageSrc: 'https://example.com/does-not-exist.jpg' },
}; Chromatic
Deterministic placeholders prevent false Chromatic diffs
Chromatic takes a screenshot of each story and compares it to the previous baseline. If the image src is a random URL or a live URL that changes, the screenshot changes every run even if the component code did not change. Chromatic flags this as a diff and requires a manual review decision.
A deterministic placeholder URL returns the exact same SVG pixel-for-pixel on every request. Chromatic's baseline stays clean. Diffs only appear when the component itself changes — which is the correct behavior.
Set `disableSnapshot: false` explicitly on your image-testing stories and `disableSnapshot: true` on stories that are known to have dynamic content (timestamps, random data) to prevent noise from leaking into baseline reviews.
CSF3 pattern
Using the play function to test image error states
CSF3's `play` function lets you write interaction tests inside a story. Test the fallback behavior by starting with a broken src and asserting that the component swaps in the placeholder after the error event fires.
export const FallbackOnError: Story = {
args: { imageSrc: 'https://example.com/broken.jpg' },
play: async ({ canvasElement }) => {
const { within, waitFor } = await import('@storybook/testing-library');
const canvas = within(canvasElement);
const img = canvas.getByRole('img');
// simulate load error
img.dispatchEvent(new Event('error'));
await waitFor(() => {
expect(img.getAttribute('src')).toContain('fallback.pics');
});
},
}; Related resources
Placeholder infrastructure for testing environments
Placeholder URLs work the same way in Playwright visual regression tests and Cypress end-to-end tests. If your CI environment blocks outbound traffic, the self-hosted Docker container provides the same URL shape locally.
https://fallback.pics/docs/
https://fallback.pics/placeholder-image-api/
https://fallback.pics/blog/figma-handoff-placeholder-urls/
https://fallback.pics/blog/playwright-deterministic-placeholders/ Key takeaways
What to standardize before shipping
- Set placeholder URLs as component-level default args so every story renders a complete, visually correct component without requiring real assets.
- Keep all placeholder constants in one file so dimension changes propagate to all stories at once.
- Deterministic placeholder URLs prevent false Chromatic diffs because the same URL always returns the same SVG.
- Add a `BrokenImage` story that tests the onerror fallback behavior explicitly alongside the happy-path story.
- Use the CSF3 `play` function to test that image error events trigger the correct fallback swap in interaction tests.
Production fallback layer
Use fallback.pics anywhere an image URL is accepted.
Start with one deterministic URL and standardize fallback behavior across your design system.