Component-Driven Development: Test UI Components in Isolation
Learn how to define repeatable component states, test rendering and interactions, and choose Storybook, Cypress, or Playwright without losing sight of integration coverage.
To test a UI component in isolation, render it with explicit props, data, providers, and controlled dependencies; then check the output and exercise the interactions that matter. Treat each meaningful state as a repeatable scenario. Stories can hold those scenarios so developers can inspect them and reuse them in render, interaction, visual, and accessibility checks.
Isolation makes component behavior easier to inspect, but it only proves behavior under the setup represented by that scenario. Keep broader tests for composition, routing, real services, and application-level behavior.
1. What component isolation means
Component-driven development uses a component as a practical unit of design and implementation. In testing, the component is rendered on its own with its inputs and dependencies made explicit. A test can then verify the rendered result and, when relevant, how it changes after user input.
For example, a search field might have separate scenarios for its ordinary state, a submitted query, an empty result, a loading state, and an error. These scenarios are more useful than generating every possible combination of props: include a case when it represents a meaningful behavior, permission, or responsive condition.
2. Build useful, repeatable scenarios
- List the meaningful states. Consider ordinary, loading, empty, error, disabled, permission-dependent, and responsive states. Include only those relevant to the component.
- Make the setup explicit. Set props and data directly. Include required providers such as a theme or router provider. Replace network calls, clocks, randomness, and other application dependencies with controlled values when the scenario needs isolation.
- Render the scenario. Inspect the component in the project’s supported test environment or story browser. Check that the state is understandable and that the intended content is present.
- Exercise behavior. Simulate relevant user actions, such as typing, clicking, or submitting a form. Assert on the visible result or state update rather than an implementation detail that users cannot observe.
- Run checks locally and in CI. Add visual comparison when the project needs a baseline review for appearance changes. Add accessibility checks where they fit the project’s workflow.
- Retain broader coverage. Test flows that depend on multiple components, routing, real services, or application configuration at an integration or end-to-end boundary.
3. Example: a small isolated React test
This example uses React Testing Library and Vitest. It demonstrates a component with explicit inputs, then checks a user action and its visible result. Use the testing libraries already supported by your project; the important pattern is controlled setup plus observable assertions.
import { describe, expect, it, vi } from 'vitest';
import { fireEvent, render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/vitest';
import SearchBox from './SearchBox';
describe('SearchBox', () => {
it('submits the entered query', () => {
const onSearch = vi.fn();
render(<SearchBox onSearch={onSearch} disabled={false} />);
fireEvent.change(screen.getByRole('textbox', { name: /search/i }), {
target: { value: 'invoice' },
});
fireEvent.click(screen.getByRole('button', { name: /search/i }));
expect(onSearch).toHaveBeenCalledWith('invoice');
});
});
The component API and accessible names in this example are illustrative: adapt them to the actual component. If the component depends on a provider, wrap it in that provider during render. If it fetches data, inject a data source or mock the request so the test does not rely on a live service.
4. Use Storybook stories as scenario definitions
A Storybook story describes an isolated use case for a component. Stories can make meaningful states easy to explore in a browser, and Storybook documents render, interaction, visual, and accessibility testing approaches. Its component testing guidance describes setting props for an initial state, simulating actions such as clicks or form entry, and checking the resulting UI and state updates.
A simple story might set a button’s label and disabled state explicitly:
import type { Meta, StoryObj } from '@storybook/react';
import SaveButton from './SaveButton';
const meta = {
component: SaveButton,
args: { label: 'Save changes', disabled: false },
} satisfies Meta<typeof SaveButton>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Ready: Story = {};
export const Disabled: Story = {
args: { label: 'Save changes', disabled: true },
};
The exact story format and test setup depend on the installed Storybook version and framework. Current Storybook guidance describes interaction tests using play functions and a Vitest addon for Vite-based projects; the versioned Storybook 8 component-testing documentation describes an earlier test-runner and interaction-addon path. Check the documentation for the version in your project before copying setup instructions.
Stories can also be reused with testing tools such as Jest, Testing Library, Vitest, and Playwright. Reusing scenario setup can reduce duplicate component configuration across tools.
5. Choose a browser-based approach that fits the project
| Approach | How it is documented | Questions to check |
|---|---|---|
| Storybook | Stories describe isolated use cases. The testing workflow covers render and interaction checks, with visual and accessibility testing as additional dimensions. Stories can be reused in other test tools. | Which Storybook version and framework are installed? Does the project use Vite and a compatible Vitest addon, or another documented test path? |
| Cypress Component Testing | Mounts a component in a real browser and supports visual inspection and browser DevTools debugging. Cypress’s React overview lists React 18 and 19 with React/Vite, React/Webpack, and Next.js combinations. | Does the project’s React version, bundler, and Cypress setup match the documented combination? How will scenarios and mocks be shared with other tests? |
| Playwright component testing | The documented approach serves a small story gallery from a development server; tests run in Node.js while components render in a real browser. Playwright’s documentation says its experimental component-testing packages were removed. | Check current Playwright guidance and package availability before adopting this approach; do not assume an older experimental setup still applies. |
Compare tools by browser fidelity, framework and bundler support, how scenarios and mocks are authored and reused, interaction and visual regression support, debugging experience, CI setup, and maintenance effort. There is no universally required choice: use the approach that fits the project’s framework, current versions, and existing workflow.
6. What isolated tests prove—and what they do not
An isolated scenario establishes how a component behaves under the inputs and setup that scenario provides. It does not, by itself, establish that the component works when assembled with the rest of the application.
- Composition: A component may work alone but receive unexpected props or ordering when combined with other components.
- Global styling: A story may omit application-wide styles or layout rules that affect the real page.
- Routing and configuration: A controlled story may not reveal a missing route, provider, feature flag, or application setting.
- Real services: Mocked dependencies do not establish that a production service responds as expected.
- End-to-end workflows: A component test cannot prove that a complete user journey works across the assembled application.
Use component checks for focused behavior and retain integration or end-to-end checks at boundaries where components, services, and application configuration meet. Storybook documents component tests and end-to-end tests as distinct testing approaches.
7. Troubleshooting isolated component tests
| Symptom | Likely cause | What to do |
|---|---|---|
| The component crashes only in the test or story environment. | A required provider, context, or application configuration is missing. | Wrap the component in the providers it needs, or supply a small controlled test decorator. Keep the scenario setup explicit. |
| A test depends on a live request or intermittently fails. | The scenario relies on an uncontrolled network dependency or timing. | Mock or inject the dependency and return deterministic data for the state under test. |
| An interaction test cannot find a control. | The control lacks the accessible role or name used by the query, or the test is looking before the UI appears. | Check the rendered accessible name and query the control by role and name. For genuinely asynchronous UI, wait for the expected visible result. |
| Storybook setup instructions do not match the installed packages. | The instructions target a different Storybook generation or testing addon. | Follow documentation for the installed version and framework. In particular, distinguish Storybook 8’s versioned component-testing path from current guidance. |
| The isolated check passes but the page is broken. | The scenario does not cover composition, global styles, routing, or real service behavior. | Add or fix a test at the integration or end-to-end boundary that owns that behavior. |
| Browser component-test setup is unsupported or no longer available. | The framework, bundler, or package combination does not match current documentation. | Verify support against the current Cypress or Playwright documentation and the project’s installed versions before adopting the setup. |
8. Visual checks with real website screenshots
Component stories help make the state under review reproducible. For a visual review that also needs the assembled website, capture a page or a specific element in a browser and compare the resulting image with the project’s chosen baseline process. A screenshot is evidence of appearance at a particular URL, viewport, and state; it does not replace assertions about component behavior.
When a screenshot API is useful, ScreenshotNeo is a website screenshot API and MCP server for developers. It can return a PNG, JPEG, WebP, or PDF from one GET request. For UI review, relevant options include a viewport or device preset, full-page capture, selecting one element by CSS selector, dark mode, retina scale, custom CSS or JavaScript, clicking an element before capture, hiding selectors, and waiting for a selector, delay, or network idle. Use a stable test URL and state, and choose a viewport that matches the visual check you intend to review.
9. Or skip the browser setup
Call the screenshot endpoint with a URL and API key. See the ScreenshotNeo API documentation for request options.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
Replace the example URL with a page that renders the component state you need. ScreenshotNeo accepts and removes cookie or consent banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
10. Performance, reliability, and cost
For component checks, keep scenarios focused: only set up providers and dependencies a scenario actually needs. Deterministic fixtures and controlled dependencies make repeated local and CI runs easier to interpret. Real-browser rendering can be useful when browser behavior or visual inspection matters, while broader application checks should cover workflows that isolation leaves out.
For screenshot review, avoid capturing more page content than the check needs: an element capture can be appropriate for a component, while full-page capture is useful for page-level layout. Waiting for a specific selector can express readiness more clearly than relying on an arbitrary delay when the page has a known target. Cache settings and other capture options are available when appropriate; choose them to match whether the check needs a fresh render or a repeatable cached result.
ScreenshotNeo pricing is Free: 1,000 shots/month with no card; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; Business: $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan. For more control over cost and workflow, it also supports caching with a chosen TTL, async jobs with signed webhooks, bulk capture of up to 100 URLs per call, and a usage API.
11. Frequently asked questions
Do I need Storybook to test components in isolation?
No. Storybook is one way to define and explore reusable scenarios. You can also render components directly in your framework’s test environment or use a supported browser component-testing workflow.
Can a visual screenshot prove that a component works?
A screenshot can show what rendered for one state and viewport. Use interaction assertions for behavior and broader tests for workflows involving the application.
How many scenarios should I create for a component?
Create scenarios for meaningful states and inputs that represent behavior you need to inspect or check. Avoid combinations that do not correspond to a real use case or a distinct behavior.
Can one story be used by more than one test tool?
Storybook documents integrations that reuse stories with tools including Jest, Testing Library, Vitest, and Playwright. Confirm the integration and syntax for the versions installed in your project.


