ScreenshotNeo

BlogGuides

Cypress Component Testing: A Practical Guide

Set up Cypress Component Testing, mount components in a real browser, build reusable test context, and understand where end-to-end tests still fit.

By the ScreenshotNeo team4 October 20269 min read

Cypress Component Testing mounts an individual UI component in a real browser, then lets you interact with it and assert what a user can see. To get started, install Cypress, open its Launchpad, choose Component Testing, review the generated framework and development-server configuration, and write a test that mounts a component, performs an action, and checks the result. Component tests complement end-to-end tests: they isolate component behavior, while broader tests check how the application’s layers work together.

1. What Cypress Component Testing covers

A component test renders a component without visiting the full deployed application. Cypress runs a development server that compiles component specs and support files with the project’s framework and bundler, then serves them to Cypress in a browser. This exercises browser behavior rather than a simulated DOM. See the Cypress Component Testing guide and framework configuration reference.

Use component tests when you want to put a UI component into a particular state, supply props, interact with it, and check its visible behavior without booting the whole app or depending on external systems. Examples include a date picker across several dates, a form that conditionally reveals fields, and design-system controls.

2. Check framework and bundler support

Choose the integration that matches your existing framework and bundler. Cypress’s current getting-started documentation lists official mount libraries for React, Angular, Vue, and Svelte. Its documented matrix includes React 18–19 with Vite 8 or Webpack 5; Next.js 15–16 with React 18–19 and Webpack 5; Vue 3 with Vite 8 or Webpack 5; Angular 21–22 with Webpack 5; and Svelte 5 with Vite 8 or Webpack 5. Cypress labels the Svelte integrations Alpha. Qwik and Lit integrations are community-maintained. These versions and labels can change, so check the current Cypress compatibility matrix before adopting the examples.

Do not change a working project’s bundler just to match a sample. The component dev server should use the project’s framework and transforms. Cypress can detect and reuse supported Vite or Webpack configuration in some setups; explicit overrides may be needed in others.

3. Install Cypress and configure component testing

Install Cypress as a development dependency using the package manager already used by the project. For npm:

npm install --save-dev cypress

Open the Cypress app:

npx cypress open
  1. Choose Component Testing in the Launchpad.
  2. Review the detected UI framework and bundler.
  3. Install any missing integration dependencies the Launchpad identifies.
  4. Let Cypress scaffold the component configuration and support files.
  5. Inspect the generated config, especially component.devServer, then select the component testing mode to open its spec runner.

The generated component.devServer configuration tells Cypress how to compile and serve the component tests. Keep the generated setup as the source of truth for your installed Cypress version and stack. The official configuration guide explains detection, supported combinations, and when to override framework or bundler settings.

4. Write a first test: mount, interact, assert

A useful first test checks the initial render, triggers a real user action, then asserts the resulting user-visible state. Here is a minimal React example. Place the component in src/Stepper.jsx:

export function Stepper({ initial = 0 }) {
  const [count, setCount] = React.useState(initial)

  return (
    <section aria-label="Stepper">
      <button onClick={() => setCount((n) => n - 1)}>Decrease</button>
      <output aria-label="Count">{count}</output>
      <button onClick={() => setCount((n) => n + 1)}>Increase</button>
    </section>
  )
}

This example assumes the project already imports React where JSX is transformed; include import React from 'react' if required by the project’s JSX setup. In cypress/component/Stepper.cy.jsx:

import { Stepper } from '../../src/Stepper'

describe('Stepper', () => {
  it('changes the displayed count when clicked', () => {
    cy.mount(<Stepper initial={2} />)

    cy.findByRole('status', { name: 'Count' }).should('have.text', '2')
    cy.findByRole('button', { name: 'Increase' }).click()
    cy.findByRole('status', { name: 'Count' }).should('have.text', '3')
  })
})

The Cypress Testing Library query in this example requires the corresponding Testing Library Cypress package and its command registration. To keep the example runnable without that extra dependency, use Cypress’s built-in queries instead:

import { Stepper } from '../../src/Stepper'

describe('Stepper', () => {
  it('changes the displayed count when clicked', () => {
    cy.mount(<Stepper initial={2} />)

    cy.get('[aria-label="Count"]').should('have.text', '2')
    cy.contains('button', 'Increase').click()
    cy.get('[aria-label="Count"]').should('have.text', '3')
  })
})

Use accessible roles and names when your project has the relevant query commands installed; otherwise built-in selectors can still target stable semantic markup. The important pattern is mount, act, assert. Cypress documents the framework mount APIs in its mount command reference.

5. Make a reusable mount command for app context

Many components rely on a router, store, theme, or other provider. Register a custom cy.mount() in the component support file so each test gets the context it needs without repeating wrapper code. Include only the providers the tested component actually depends on.

// cypress/support/component.jsx
import React from 'react'
import { mount } from 'cypress/react'
import { MemoryRouter } from 'react-router-dom'
import { AppThemeProvider } from '../../src/theme'

Cypress.Commands.add('mount', (component, options = {}) => {
  const wrapped = (
    <MemoryRouter>
      <AppThemeProvider>{component}</AppThemeProvider>
    </MemoryRouter>
  )

  return mount(wrapped, options)
})

Adapt the mount import to the framework integration generated for the project, and replace the example providers with the app’s real ones. If no additional context is needed, use Cypress’s generated mount command directly. A custom mount can also accept options such as a test-specific route or store state; keep those options explicit so a test’s setup is easy to understand.

6. Load representative styles and global setup

A component may mount correctly and still look unlike the application if the test environment lacks global CSS, fonts, resets, runtime initialization, or app context. Load the relevant setup from the component support file or cypress/support/component-index.html, as described in the Cypress styling components guide.

// cypress/support/component.jsx
import '../../src/styles/global.css'
import './commands'

Keep setup representative but focused. Loading the entire app’s runtime can add dependencies and make isolated tests fragile; omitting a font or reset can make layout assertions misleading. Browser assertions about visibility, dimensions, and overflow are only useful when the styles that affect those properties are present.

7. Build coverage in useful steps

  1. Default render: verify the component displays its expected initial content.
  2. Alternate props: cover meaningful configurations, such as disabled, compact, or preselected states.
  3. Interaction: click, type, select, or submit and assert the visible outcome.
  4. Callbacks: use a Cypress spy when the component contract includes calling a parent handler.
  5. Boundary states: cover empty, loading, validation-error, and failure states that the component itself owns.
  6. Visual contract: add focused visibility or layout checks when they are part of the component’s expected behavior.

For an event callback, a spy keeps the assertion tied to the component’s public behavior:

const onChange = cy.spy().as('onChange')
cy.mount(<MyInput onChange={onChange} />)
cy.get('input').type('hello')
cy.get('@onChange').should('have.been.called')

Prefer assertions about what users can observe or what the component promises to its parent. Avoid coupling every test to internal state or implementation details that can change without changing behavior.

8. Component tests versus end-to-end tests

Dimension Component test End-to-end test
Scope An individual component in isolation An application workflow across layers
Setup Mount a component with props and required context Visit the app and exercise it in its running environment
Good at Putting a component into specific states and checking its behavior Checking that routing, UI, services, and other layers work together
Does not prove That the complete deployed app integrates correctly Every component state is covered efficiently

A successful component suite does not prove that routing, backend integration, or multiple app layers work together. Keep end-to-end or other broader tests for workflows where those integrations matter. Cypress recommends combining test types to cover both isolated behavior and system integration; see its guide to testing types.

9. Capture a component’s appearance

When a component has rendered in the Cypress browser, you can capture the runner’s visible page with a screenshot command:

cy.screenshot('stepper-component')

This is useful for inspecting a local test state or retaining a screenshot artifact from a run. It captures the Cypress test browser context, which includes the runner environment; it is not the same as capturing a clean screenshot of a public website. Cypress screenshot options and behavior are documented in the screenshot command reference.

Or skip the browser setup

If your goal is a screenshot of a public page rather than a mounted component test, ScreenshotNeo returns an image or PDF from one GET request. Its options include viewport and device presets, full-page or selector capture, dark mode, custom CSS and JavaScript, waits, headers and cookies, resource blocking, caching, and async or bulk capture. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie and consent banners are accepted like a visitor; more than 60 known consent platforms, newsletter popups, and chat widgets can be removed before the shot, and each step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Response headers report the page verdict and billing status.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
  • The free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots; all features are available on every plan.

Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.

10. Troubleshooting

Symptom Likely cause What to check
Launchpad does not detect the framework The project dependencies or config are not in the directory Cypress opened, or the stack is not supported by the selected integration. Open Cypress from the project root; verify the installed framework and bundler against Cypress’s current support matrix.
Development server fails to start Missing integration dependency, incompatible versions, or config that Cypress cannot detect or reuse. Review the Launchpad’s dependency checks and generated component.devServer; compare it with the official framework configuration guide.
cy.mount is undefined The component support file did not register the command, or the spec is running with an unexpected support-file configuration. Check the generated component support file and component config’s support-file setting. Ensure the mount command is imported or registered there.
Component mounts but context-dependent code fails A required provider, router, plugin, or initialization step is missing. Add the minimum required context in a reusable custom mount and supply test-specific state explicitly.
It renders unstyled or layout checks differ Global CSS, fonts, resets, or runtime setup are not loaded in the component environment. Load relevant assets through the support file or component index HTML, then recheck viewport-sensitive assertions.
Works in isolation but fails in the app The isolated test omits integration behavior, such as routing or service interaction. Add or retain a broader end-to-end test for the workflow across those layers.

11. Performance, reliability, and cost

Component tests avoid starting the complete deployed application for each component state, and they make individual states straightforward to exercise. Their reliability still depends on a coherent development-server configuration, representative styles and providers, and assertions tied to observable behavior. Keep shared setup small and deterministic; avoid loading unrelated app services into every mount.

Cypress itself is free and open source. Cypress Cloud is an optional paid companion service for recordings and analytics; it is not required to write or run component tests. See Why Cypress and Cypress Cloud introduction for current product details. Budget engineering time for maintaining the component test environment and for broader tests that cover integrated workflows. No independent runtime or cost benchmark is asserted here.

12. FAQ

Does component testing require a deployed site?

No. Cypress starts a development server to compile and serve the component test environment.

Can I use component tests instead of end-to-end tests?

They cover different scopes. Use broader tests for behavior that depends on the application’s layers working together.

Should every component have a test?

Prioritize components with meaningful behavior, state variations, or user impact. A test is most useful when it protects a clear contract.

Where can I check which integrations are current?

Use Cypress’s getting-started compatibility matrix when setting up or upgrading; framework and bundler support changes over time.