Modern test suites mix component and E2E tests at different ratios. Component tests give fast feedback on UI units; E2E tests give high signal on user journeys. This guide explains when to invest in each, with code samples and a real ratio recommendation.
What is component testing?
A component test mounts a single UI component in isolation and asserts on its behaviour. No server, no router, no full app. Cypress Component Testing supports React, Vue, Angular, and Svelte.
Component vs E2E trade-offs
- Component — fast (sub-second), cheap to maintain, narrow scope.
- E2E — slow (seconds to minutes), catches integration issues, broad scope.
A practical ratio
- 70% component tests (covers UI logic in isolation).
- 20% integration tests (covers flows across 2-3 components + API mocks).
- 10% E2E tests (covers the critical user journeys end to end).
Code example: Cypress component test
Where component tests shine
- UI units with branching logic — buttons, modals, forms, dropdowns with multiple states.
- Edge-case rendering — empty states, error states, loading skeletons, long strings, RTL languages.
- Component-level accessibility — `cy.injectAxe()` runs axe-core against the mounted component in milliseconds.
- Visual regression — pair component tests with Cypress's screenshot plugin for pixel-level diffing.
Where E2E tests still win
- Routing and deep linking — verify guards, redirects, query params.
- Auth + session flows — sign-in, sign-up, password reset, MFA challenges.
- Cross-page integrations — checkout, onboarding, anything that touches several services.
- Real browser engine quirks — only a real E2E run hits engine-specific behaviour that component tests skip.
Network mocking strategies
Component tests stub the network via `cy.intercept()` so a single component is tested in isolation. E2E tests prefer real services for happy paths, but stub flaky or third-party APIs. The same `cy.intercept()` API works in both modes; only the policy differs.
- Component — stub everything by default; use realistic fixture data.
- E2E happy path — real services; stub only what is slow or non-deterministic.
- E2E error path — explicitly stub 5xx, 429, network drop to assert the UI handles failure.
CI pipeline layout
- Pre-merge — run component tests on every PR. Sub-second feedback, no flake, parallel by file.
- Post-merge — run E2E on the main branch and against staging before each release.
- Nightly — full cross-browser E2E across Chromium, Firefox, and WebKit, including visual diffs.
- Recordings — store Cypress Cloud videos and screenshots for failed runs only, to keep storage lean.
Common pitfalls
- Over-stubbing — component tests with full API mocks lose integration value. Keep stubs minimal.
- Sharing state across tests — Cypress isolates by default; do not persist state via `window` between tests.
- Mounting the wrong tree — mount the leaf, not the parent page. Use wrappers only for providers.
- Ignoring a11y in E2E — `cy.injectAxe()` works in E2E too; run it on a few key pages.
FAQ
- Does Cypress component testing replace Jest + RTL? No. Use Cypress component tests for behaviour-driven UI specs; keep Jest + RTL for fast logic-only unit tests.
- Can I use Storybook stories as Cypress component tests? Yes, via cypress-storybook or by mounting the story directly.
- How slow is E2E in CI? A 100-test E2E suite typically runs in 4–8 minutes on a 4-vCPU runner with Cypress's parallelisation.
- Does component testing support Vue and Angular? Yes. Cypress ships first-party adapters for React, Vue, Angular, Svelte, and Next.js.
Setup walkthrough and project layout
Setting up Cypress Component Testing is a 5-minute job in any modern frontend project. Install Cypress as a dev dependency, add the component-testing adapter for your framework (React, Vue, Angular, Svelte, or Next.js), and create a cypress.config.{js,ts} file. Cypress auto-discovers component specs in the cypress folder you point it at.
- Install — `npm install -D cypress @cypress/react` (replace react with the adapter for your framework).
- Config — `cypress.config.ts` with `component.specPattern: "src/**/*.cy.{js,jsx,ts,tsx}"`.
- Run — `npx cypress open --component` for the GUI, `npx cypress run --component` for CI.
- Project layout — co-locate `.cy.tsx` files next to the component they test, or keep them under `cypress/component/`. Co-location makes it obvious when a test is missing for a new component.
Patterns: props, events, accessibility, visual regression
Once mounted, a Cypress component test exercises the component the same way a user does — click, type, hover, focus. The best tests verify user-observable behaviour (the rendered DOM, accessible roles, emitted events) rather than internal state.
- Props — pass props directly to the mounted component; no need for context providers unless the component depends on them.
- Events — use `cy.spy()` or `cy.stub()` on the callback prop; assert with `should("have.been.calledWith", payload)`.
- Accessibility — call `cy.injectAxe()` once, then `cy.checkA11y()` per assertion. Catches contrast, label, and role regressions in milliseconds.
- Visual regression — pair with Percy, Chromatic, or Applitools. Cypress captures stable screenshots thanks to deterministic waits.
When to escalate to E2E
Component tests cover most UI logic. The remainder — routing, multi-page flows, real third-party SDKs, performance budgets — belongs in E2E. A reasonable heuristic is the test pyramid: roughly 70% unit/component, 20% integration, 10% E2E. If a test would require a router or a real backend, it is probably an integration or E2E test, not a component test.
- Routing — belongs in E2E (component tests do not include the router by default).
- Auth flows — E2E; mock the IdP in staging and use a real browser.
- Third-party SDKs — Stripe Elements, Google Maps, analytics — E2E with sandbox keys.
- Performance — Lighthouse CI or k6 against staging; not a Cypress job.






