What Is Cypress Component Testing
Cypress Component Testing (CCT) mounts an individual UI component, such as a single React, Vue, Angular, or Svelte component, into a real browser using your project's actual bundler (Vite or Webpack) rather than a simulated DOM like jsdom. Because the component renders in a genuine browser with real CSS, layout, and event handling, tests can assert on things unit tests with jsdom cannot reliably verify, such as computed styles, real click coordinates, or CSS transitions. Specs live under cypress/component by convention and use the same cy commands as end-to-end tests, but the entry point is cy.mount() instead of cy.visit().
Cricket analogy: It's like testing a single bowler's yorker in a real net session with an actual pitch and ball, instead of just analyzing arm-action video in isolation — you get real physics, not a simulation.
Setting Up and Mounting Components
Component testing is configured in a separate component block inside cypress.config.js, which specifies a devServer (for example, a Vite or Webpack dev server via a framework-specific plugin like @cypress/react or @cypress/vue) and a specPattern such as 'src/**/*.cy.{js,jsx,ts,tsx}'. This devServer is what actually compiles and serves each component in isolation, resolving imports, CSS modules, and other bundler-specific features exactly as the real app would. Running npx cypress open --component launches a picker for these specs, separate from the e2e picker.
Cricket analogy: It's like a franchise setting up a dedicated indoor practice net with its own turf and lighting rig, distinct from the main stadium, specifically to isolate individual skill drills.
The cy.mount() command, imported from the framework adapter, is called at the start of each test to render the component with specific props, for example cy.mount(<PriceTag price={19.99} currency="USD" />). After mounting, you interact with the rendered output using the same cy.get(), cy.click(), and cy.type() commands used in end-to-end tests, and you can pass a Cypress stub as a prop callback, like onAddToCart={cy.stub().as('addToCart')}, then assert it was called with cy.get('@addToCart').should('have.been.calledOnce') to verify the component invokes its callback props correctly.
Cricket analogy: It's like feeding a bowling machine a specific pre-set delivery (props) and then checking exactly how the batter's bat makes contact, isolating one interaction at a time.
Interacting and Asserting
Because the component is mounted in isolation, assertions can be narrower and more precise than in a full end-to-end flow: you can assert on a specific class toggling when a prop changes, on ARIA attributes for accessibility, or on the exact number of times a re-render occurred by combining cy.mount() with a wrapped rerender helper. This precision makes component tests faster to write and less brittle than an equivalent end-to-end test would be for the same UI logic, since there is no need to first navigate through the application to reach the component's state.
Cricket analogy: It's like a batting coach isolating just the footwork against spin in a solo drill, rather than needing a full match situation to assess that one technical detail.
// PriceTag.cy.jsx
import React from 'react';
import PriceTag from './PriceTag';
describe('<PriceTag />', () => {
it('renders formatted price and fires onAddToCart', () => {
const onAddToCart = cy.stub().as('addToCart');
cy.mount(
<PriceTag price={19.99} currency="USD" onAddToCart={onAddToCart} />
);
cy.get('[data-cy=price]').should('have.text', '$19.99');
cy.get('[data-cy=add-to-cart]').click();
cy.get('@addToCart').should('have.been.calledOnceWith', { id: undefined, price: 19.99 });
});
});
// cypress.config.js (component block)
module.exports = {
component: {
devServer: {
framework: 'react',
bundler: 'vite',
},
specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}',
},
};Component testing bridges the gap between fast, isolated unit tests and slow, full-stack end-to-end tests: it gives you real-browser rendering fidelity while still testing one component at a time, which typically makes suites run significantly faster than equivalent end-to-end coverage.
Component tests should not replace end-to-end tests entirely. They cannot verify routing between pages, real API integration, or multi-page user journeys, since the component is rendered outside the full application shell — keep a smaller set of true end-to-end specs for critical user flows.
- Cypress Component Testing mounts a single component in a real browser using your project's actual bundler.
- cy.mount() is the component-testing entry point, used in place of cy.visit().
- Component specs live under cypress/component by convention and use a separate devServer config block.
- You can pass cy.stub() as a prop callback and assert it was called to verify component behavior.
- Component tests give precise, narrow assertions without needing to navigate the full application first.
- CCT complements but does not replace end-to-end tests, which still cover full user journeys and API integration.
- Running npx cypress open --component opens a picker separate from the e2e spec picker.
Practice what you learned
1. What command is used to render a component at the start of a Cypress component test?
2. Which of the following can a component test verify that a jsdom-based unit test typically cannot?
3. How can you verify a mounted component correctly invokes a callback prop?
4. Where is the devServer for component testing configured?
5. What is a key limitation of component testing compared to end-to-end testing?
Was this page helpful?
You May Also Like
Visual Testing and Screenshots
Learn how Cypress captures screenshots automatically and manually, and how visual regression plugins detect unwanted UI changes.
Environment Variables and Configuration
Learn how Cypress resolves environment variables across config files, OS variables, and the CLI, and how fixtures supply deterministic test data.