Founder & Lead Engineer, RAITHub
RAITHub ships and tests production software. See QA as a Service or talk to us.
Testing a React app means two layers that do different jobs: component tests that check a piece of UI behaves correctly for a user, and end-to-end tests that drive the whole app in a real browser through the journeys that matter. Component tests catch most bugs fast; a small end-to-end set proves the pieces work together. RAITHub does this as a plan, an audit or a managed team.
If you would rather have the suite built and kept green for you, see how RAITHub would test this near the end.
What should a React test suite cover?
React apps fail in predictable places: forms that accept bad input, components that break on empty or error states, async data that renders before it arrives, and journeys that work step by step but break when strung together. A good suite covers each at the lowest layer that can catch it.
| Layer | What it covers | Typical tool |
|---|---|---|
| Unit | Pure logic: formatters, validation, reducers, hooks' return values | Vitest or Jest |
| Component | A component as a user meets it: it renders, responds to clicks and typing, shows loading, empty and error states | Testing Library with Vitest/Jest, or Playwright component tests |
| End-to-end | Whole journeys in a real browser: sign-up, sign-in, the core action, checkout | Playwright or Cypress |
Most of the protection comes from the lower two layers, which run in milliseconds to seconds. A handful of end-to-end tests prove the whole thing holds together; a suite that is all end-to-end tests is slow and flaky. That shape is the test pyramid, in the test pyramid for a SaaS.
What does a good React component test look like?
Test the component the way a user meets it, not its internals. The Testing Library guiding principle is that the more your tests resemble the way your software is used, the more confidence they give you (Testing Library guiding principles). So query by what the user sees (roles, labels, text), act as the user acts, and assert on what appears. This test checks a login form validates and submits:
// LoginForm.test.tsx
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { LoginForm } from './LoginForm'
test('shows an error when the password is empty', async () => {
const onSubmit = vi.fn()
render(<LoginForm onSubmit={onSubmit} />)
await userEvent.type(screen.getByLabelText(/email/i), 'a@example.com')
await userEvent.click(screen.getByRole('button', { name: /sign in/i }))
expect(screen.getByText(/password is required/i)).toBeInTheDocument()
expect(onSubmit).not.toHaveBeenCalled()
})
The test never reaches into state or props directly. It fills the form, clicks the button, and checks the error the user would see. That keeps the test valid when the component is refactored, as long as the behaviour is the same.
Component tests or end-to-end tests: which, for what?
Use component tests for anything you can prove about one piece of UI in isolation: validation, conditional rendering, loading and error states, accessibility of a control. They are fast and precise. Use end-to-end tests only for whole journeys that cross pages, data and the real backend: sign-up through to the core action, checkout, an invite flow.
Free checklist
AI-Built App Launch Readiness Checklist
25 checks before you let real users in. Enter your email and we’ll reveal it below (and send you a copy).
One email, the checklist, no spam. By submitting you agree we can email you this checklist and reply to your enquiry.
| Question | Component test | End-to-end test |
|---|---|---|
| Does this form validate? | Yes, cheaply | Overkill |
| Does the empty state render? | Yes | No |
| Can a user sign up and reach the dashboard? | No | Yes |
| Does checkout take a real payment in sandbox? | No | Yes |
The common React mistake is pushing everything to end-to-end tests because they feel more "real". The result is a slow, flaky suite that fails for reasons unrelated to the change. Keep end-to-end tests to the five to fifteen journeys that earn money or hold data.
Which tools fit a React app?
React apps are JavaScript or TypeScript, so the tools stay in the same language: Vitest or Jest as the runner, Testing Library for component tests, and Playwright or Cypress for end-to-end. Playwright drives real Chromium, Firefox and WebKit from one API (Playwright docs), which also covers the cross-browser pass; see the cross-browser testing checklist. If your stack already uses Cypress, Playwright vs Cypress in 2026 compares the two.
The React-specific traps are async and mocking. Tests must wait for data to load rather than asserting too early, and calls to your backend should be stubbed at the network boundary so component tests stay fast and deterministic. Flaky waits are the usual cause of a suite nobody trusts; fixing flaky tests explains the method. Whatever the runner, the tests belong in your repository and run in CI on every pull request.
Buy, build or hire?
| Option | Choose this when | Watch out for |
|---|---|---|
| A tool or SaaS testing platform | You want recorded browser checks fast, with little code | Recorded tests break often and live outside your repository |
| Freelancers or crowdtesting | You need a one-off manual pass before a launch | No suite remains, and no one maintains it |
| An in-house QA hire | You release weekly and want QA on the team long-term | Needs someone who writes JavaScript or TypeScript, not only manual tests |
| A managed QAaaS team | You want component and end-to-end tests built, gated in CI and kept current | Make sure the tests live in your repository and you own them |
Why RAITHub for testing a React app?
Because React is part of RAITHub's own stack, the engineers who write the tests also build React UIs, so they test components the way users meet them and keep end-to-end tests to the journeys that matter. The test counts are from real repositories: this site runs 400+ tests in CI; PropDesk has 1,024; TheSkinProof, the founder's own venture, has 750+ across its portals. How that discipline works is in QA-first development: how RAITHub tests.
When you don't need RAITHub: if you want an engineer placed inside your team under your management, that is staff augmentation, which RAITHub does not offer; and an early prototype you reshape weekly may only need a short manual pass. For the broader method of having tests written across layers, see testing a Next.js app, which shares the same approach for React-based server apps.
How RAITHub would test this
Through QA and test automation, part of QA as a service, RAITHub would:
- map your components and journeys, and rank the journeys by cost of failure
- write component tests with Testing Library for validation, states and interactions, stubbing the network at the boundary
- add a small Playwright or Cypress set for the journeys that earn money or hold data
- wire the suite into your CI so a failing change blocks the release, with tests and configuration in your repository
- keep the tests current as the UI changes, and report what was caught
Buy it as a monthly QA plan, a fixed-price one-off audit, or a dedicated QA team that RAITHub manages and bills monthly. Testers are never placed under your management. You own the tests and the IP, and an NDA is signed first. Start with a free 15-minute call, then a written fixed quote. Talk to RAITHub about testing your React app.
Frequently asked questions
How do I test a React app?
Unit-test pure logic, write component tests with Testing Library for validation, states and interactions, and drive the handful of journeys that matter with Playwright or Cypress in a real browser, all running in CI on every change.
What is the difference between component tests and end-to-end tests?
A component test checks one piece of UI in isolation, fast and precise. An end-to-end test drives the whole app in a real browser through a journey that crosses pages and the real backend. Use component tests for most things and end-to-end tests only for key journeys.
Should I use Testing Library or Playwright?
Both, for different layers. Testing Library (with Vitest or Jest) runs component tests close to the code; Playwright runs whole-browser journeys. Playwright also offers component testing, but most React teams keep Testing Library for components and Playwright for end-to-end.
Why are my React tests flaky?
Usually because they assert before async data has loaded, or depend on another test's leftover state. Wait for what the user would wait for, give each test its own data, and stub the network at the boundary so results are deterministic.
Can RAITHub add tests to a React app it did not build?
Yes. RAITHub starts with the journeys that cost most when they break, writes tests that pin down current behaviour, and gates them in CI before widening coverage or changing any code.
Will I own the tests?
Yes. The tests and CI configuration live in your repository, the IP is assigned to you, and the engagement runs month-to-month, so the suite keeps protecting you after it ends.
Related posts
Ready to discuss your project?
Book a free 15-minute technical audit with our engineering team.