---
id: app/write-tests/component-testing/react/examples
title: React examples
description: >-
  Learn how to mount a React component, pass data to a React component, test
  event handlers, and customize cy.mount() for React Router and Redux.
section: app
source_path: docs/app/write-tests/component-testing/react/examples.mdx
version: 6e2098c7a43dfc945897ed4a85d64ced1975a50f
updated_at: '2026-09-11T16:43:10.614Z'
---
# React Examples

The first step in testing a component is to mount it. This renders the component into a testbed and enable's the use of the Cypress API to select elements, interact with them, and run assertions.

To mount a React component, import the component into your spec and pass the component to the `cy.mount` command:

```
import { Stepper } from './stepper'

it('mounts', () => {
  cy.mount(<Stepper />)
  //Stepper should have initial count of 0 (default)
  cy.get('[data-cy=counter]').should('have.text', '0')
})
```

## Passing Data to a Component

You can pass props to a component by setting them on the JSX passed into `cy.mount()`:

```
it('mounts', () => {
  cy.mount(<Stepper initial={100} />)
  //Stepper should have initial count of 100
  cy.get('[data-cy=counter]').should('have.text', '100')
})
```

## Testing Event Handlers

Pass a Cypress [spy](/llm/markdown/app/guides/stubs-spies-and-clocks.md#Spies) to an event prop and validate it was called:

```
it('clicking + fires a change event with the incremented value', () => {
  const onChangeSpy = cy.spy().as('onChangeSpy')
  cy.mount(<Stepper onChange={onChangeSpy} />)
  cy.get('[data-cy=increment]').click()
  cy.get('@onChangeSpy').should('have.been.calledWith', 1)
})
```

## Testing Error States

`cy.mount()` is an asynchronous Cypress command: it _enqueues_ the mount and returns immediately, before the component ever renders. When a component throws during render, the error surfaces as an uncaught exception rather than as a synchronous throw.

By default Cypress fails the test on any uncaught exception, so to assert on a render error you listen for it with [`cy.on('uncaught:exception')`](/llm/markdown/api/cypress-api/catalog-of-events.md#Uncaught-Exceptions) and return `false` to prevent Cypress from failing the test:

```
import { UserProfile } from './UserProfile'

it('surfaces a render error', () => {
  cy.on('uncaught:exception', (err) => {
    // Assert on the error thrown during render...
    expect(err.message).to.include('user is required')

    // ...and return false so Cypress does not fail the test.
    return false
  })

  cy.mount(<UserProfile />)
})
```

### Testing Error Boundaries

The recommended way to handle render errors in React is an [Error Boundary](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary), which renders fallback UI via `getDerivedStateFromError()`. You can mount a component wrapped in your Error Boundary and assert that the fallback UI is displayed:

```
// ErrorBoundary.cy.jsx
import { ErrorBoundary } from './ErrorBoundary'

const errorMessage = 'I crashed!'
const ChildWithError = () => {
  throw new Error(errorMessage)
}

it('displays the fallback UI on error', () => {
  // An Error Boundary renders fallback UI, but it does NOT stop the error
  // from propagating to Cypress as an uncaught exception. Cypress fails on
  // uncaught exceptions by default, so we must still suppress that behavior.
  cy.on('uncaught:exception', (err) => {
    // Only suppress the specific error we expect.
    expect(err.message).to.include(errorMessage)

    return false
  })

  cy.mount(
    <ErrorBoundary name="ChildWithError">
      <ChildWithError />
    </ErrorBoundary>
  )

  cy.get('header h1').should('contain', 'Something went wrong.')
})
```

Where `ErrorBoundary` renders fallback UI from `getDerivedStateFromError()`:

```
// ErrorBoundary.jsx
import React from 'react'

export class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props)
    this.state = { error: null }
  }

  static getDerivedStateFromError(error) {
    return { error }
  }

  render() {
    const { name } = this.props
    const { error } = this.state

    if (error) {
      return (
        <header>
          <h1>Something went wrong.</h1>
          <h2>{`${name} failed to load`}</h2>
        </header>
      )
    }

    return this.props.children
  }
}
```

## Custom Mount Commands

### Customizing `cy.mount()`

By default, `cy.mount()` is a simple passthrough to `mount()`, however, you can customize `cy.mount()` to fit your needs. For instance, if you are using providers or other global app-level setups in your React app, you can configure them here.

Below are a few examples that demonstrate using a custom mount command. These examples can be adjusted for most other providers that you will need to support.

### React Router

If you have a component that consumes a hook or component from [React Router](https://reactrouter.com/), make sure the component has access to a React Router provider. Below is a sample mount command that uses `MemoryRouter` to wrap the component.

*   cypress/support/component.jsx
*   cypress/support/component.tsx

```
import { mount } from 'cypress/react'
import { MemoryRouter } from 'react-router-dom'

Cypress.Commands.add('mount', (component, options = {}) => {
  const { routerProps = { initialEntries: ['/'] }, ...mountOptions } = options

  const wrapped = <MemoryRouter {...routerProps}>{component}</MemoryRouter>

  return mount(wrapped, mountOptions)
})
```

```
import { mount, MountOptions, MountReturn } from 'cypress/react'
import { MemoryRouter, MemoryRouterProps } from 'react-router-dom'

declare global {
  namespace Cypress {
    interface Chainable {
      /**
       * Mounts a React node
       * @param component React Node to mount
       * @param options Additional options to pass into mount
       */
      mount(
        component: React.ReactNode,
        options?: MountOptions & { routerProps?: MemoryRouterProps }
      ): Cypress.Chainable<MountReturn>
    }
  }
}

Cypress.Commands.add('mount', (component, options = {}) => {
  const { routerProps = { initialEntries: ['/'] }, ...mountOptions } = options

  const wrapped = <MemoryRouter {...routerProps}>{component}</MemoryRouter>

  return mount(wrapped, mountOptions)
})
```

To set up certain scenarios, pass in props that will get passed to `MemoryRouter` in the options. Below is an example test that ensures an active link has the correct class applied to it by initializing the router with `initialEntries` pointed to a particular route:

```
import { Navigation } from './Navigation'

it('home link should be active when url is "/"', () => {
  // No need to pass in custom initialEntries as default url is '/'
  cy.mount(<Navigation />)

  cy.get('a').contains('Home').should('have.class', 'active')
})

it('login link should be active when url is "/login"', () => {
  cy.mount(<Navigation />, {
    routerProps: {
      initialEntries: ['/login'],
    },
  })

  cy.get('a').contains('Login').should('have.class', 'active')
})
```

### Redux

To use a component that consumes state or actions from a [Redux](https://react-redux.js.org/) store, create a `mount` command that will wrap your component in a Redux Provider:

*   cypress/support/component.jsx
*   cypress/support/component.tsx

```
import { mount } from 'cypress/react'
import { Provider } from 'react-redux'
import { getStore } from '../../src/store'

Cypress.Commands.add('mount', (component, options = {}) => {
  // Use the default store if one is not provided
  const { reduxStore = getStore(), ...mountOptions } = options

  const wrapped = <Provider store={reduxStore}>{component}</Provider>

  return mount(wrapped, mountOptions)
})
```

```
import { mount, MountOptions, MountReturn } from 'cypress/react'
import { Provider } from 'react-redux'
import { EnhancedStore } from '@reduxjs/toolkit'
import { getStore } from '../../src/store'
import { RootState } from './src/StoreState'

declare global {
  namespace Cypress {
    interface Chainable {
      /**
       * Mounts a React node
       * @param component React Node to mount
       * @param options Additional options to pass into mount
       */
      mount(
        component: React.ReactNode,
        options?: MountOptions & { reduxStore?: EnhancedStore<RootState> }
      ): Cypress.Chainable<MountReturn>
    }
  }
}

Cypress.Commands.add('mount', (component, options = {}) => {
  const { reduxStore = getStore(), ...mountOptions } = options

  const wrapped = <Provider store={reduxStore}>{component}</Provider>

  return mount(wrapped, mountOptions)
})
```

The options param can have a store that is already initialized with data:

```
import { getStore } from '../redux/store'
import { setUser } from '../redux/userSlice'
import { UserProfile } from './UserProfile'

it('User profile should display user name', () => {
  const user = { name: 'test person' }

  // getStore is a factory method that creates a new store
  const store = getStore()

  // setUser is an action exported from the user slice
  store.dispatch(setUser(user))

  cy.mount(<UserProfile />, { reduxStore: store })

  cy.get('div.name').should('have.text', user.name)
})
```

The `getStore` method is a factory method that initializes a new Redux store. It is important that the store be initialized with each new test to ensure changes to the store don't affect other tests.
