How to Replace React Testing Library with Vitest Browser Mode

Vitest Browser Mode allows you to run tests in real browsers instead of jsdom. This guide shows you how to migrate from React Testing Library to Vitest Browser Mode.
Installation
First, install the required packages:
bashnpm install -D vitest @vitest/browser @vitest/browser-playwright vitest-browser-react playwright
Configuration
Update your vitest.config.ts:
typescriptimport { defineConfig } from 'vitest/config' import react from '@vitejs/plugin-react' import { playwright } from '@vitest/browser-playwright' export default defineConfig({ plugins: [react()], test: { browser: { provider: playwright(), enabled: true, instances: [{ browser: 'chromium' }], }, }, })
Migration Guide
Before: React Testing Library
tsximport { render, screen, fireEvent } from '@testing-library/react' import { expect, test } from 'vitest' import Counter from './Counter' test('increments counter', () => { render(<Counter />) const button = screen.getByRole('button', { name: /increment/i }) const count = screen.getByText(/count: 0/i) expect(count).toBeInTheDocument() fireEvent.click(button) expect(screen.getByText(/count: 1/i)).toBeInTheDocument() })
After: Vitest Browser Mode
tsximport { render } from 'vitest-browser-react' import { page, userEvent } from 'vitest/browser' import { expect, test } from 'vitest' import Counter from './Counter' test('increments counter', async () => { render(<Counter />) const button = page.getByRole('button', { name: /increment/i }) const count = page.getByText(/count: 0/i) await expect.element(count).toBeInTheDocument() await userEvent.click(button) await expect.element(page.getByText(/count: 1/i)).toBeInTheDocument() })
Key Differences
1. Imports
RTL:
tsximport { render, screen } from '@testing-library/react'
Browser Mode:
tsximport { render } from 'vitest-browser-react' import { page } from 'vitest/browser'
2. Querying Elements
RTL:
tsxscreen.getByRole('button') screen.getByText(/hello/i)
Browser Mode:
tsxpage.getByRole('button') page.getByText(/hello/i)
3. Assertions
RTL:
tsxexpect(button).toBeInTheDocument()
Browser Mode:
tsxawait expect.element(button).toBeInTheDocument()
4. User Interactions
RTL:
tsximport userEvent from '@testing-library/user-event' await userEvent.click(button) await userEvent.type(input, 'hello')
Browser Mode:
tsximport { userEvent } from 'vitest/browser' await userEvent.click(button) await userEvent.fill(input, 'hello')
Form Example
tsximport { render } from 'vitest-browser-react' import { page, userEvent } from 'vitest/browser' import { expect, test } from 'vitest' test('submits login form', async () => { render(<LoginForm />) const emailInput = page.getByLabelText(/email/i) const passwordInput = page.getByLabelText(/password/i) const submitButton = page.getByRole('button', { name: /sign in/i }) await userEvent.fill(emailInput, '[email protected]') await userEvent.fill(passwordInput, 'password123') await userEvent.click(submitButton) await expect.element(page.getByText(/welcome back/i)).toBeInTheDocument() })
Async Data Example
tsxtest('displays user profile', async () => { render(<UserProfile userId="123" />) // Wait for loading state to disappear await expect.element(page.getByText(/loading/i)).not.toBeInTheDocument() // Check data is displayed await expect.element(page.getByText(/john doe/i)).toBeInTheDocument() })
Running Tests
bash# Run all tests npm run vitest # Run specific file npm run vitest src/components/Button.test.tsx # Run with UI npm run vitest -- --ui
Browser Options
You can test in different browsers:
typescriptexport default defineConfig({ test: { browser: { provider: playwright(), enabled: true, instances: [ { browser: 'chromium' }, { browser: 'firefox' }, { browser: 'webkit' }, ], }, }, })
Benefits
- Tests run in real browsers, not jsdom
- More accurate browser behavior
- Better debugging with browser DevTools
- Support for multiple browsers
- Visual testing capabilities
Tips
- All assertions must use
await expect.element() - All user interactions must be awaited
- Use
pageinstead ofscreenfor queries - Import render from
vitest-browser-react, not@testing-library/react - Use
userEvent.fill()instead ofuserEvent.type()for inputs