How to Mock useParams Hook in React

Testing React components that rely on dynamic URL parameters requires mocking the useParams hook from react-router-dom. This article provides a straightforward guide on how to mock this hook using Jest and React Testing Library, covering both static module mocking and dynamic parameter changes for individual test cases.

Method 1: Mocking useParams globally with Jest

The most direct way to mock useParams is by using jest.mock(). This approach intercepts the react-router-dom module and returns a custom value for the hook while preserving the rest of the library’s functionality.

Add the following block at the top of your test file:

import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';

// Mock the react-router-dom library
jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useParams: () => ({ id: '123' }),
}));

test('renders component with mock parameter', () => {
  render(<MyComponent />);
  expect(screen.getByText(/User ID: 123/i)).toBeInTheDocument();
});

Using jest.requireActual ensures that other essential router components, like Link or Navigate, still function normally during the test run.


Method 2: Dynamic mocking for different test cases

If you need to test how your component behaves with different URL parameters across multiple tests, you can assign a jest.fn() to a variable and change its implementation dynamically.

import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';

const mockUseParams = jest.fn();

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useParams: () => mockUseParams(),
}));

describe('MyComponent with dynamic params', () => {
  beforeEach(() => {
    mockUseParams.mockReset();
  });

  test('handles user ID 123', () => {
    mockUseParams.mockReturnValue({ id: '123' });
    render(<MyComponent />);
    expect(screen.getByText(/User ID: 123/i)).toBeInTheDocument();
  });

  test('handles user ID 999', () => {
    mockUseParams.mockReturnValue({ id: '999' });
    render(<MyComponent />);
    expect(screen.getByText(/User ID: 999/i)).toBeInTheDocument();
  });
});

While explicitly mocking the hook works well, the recommended practice in React Testing Library is to avoid mocking library internals. Instead, wrap your component in a MemoryRouter and define a route with the parameter you want to test.

import { render, screen } from '@testing-library/react';
import { MemoryRouter, Routes, Route } from 'react-router-dom';
import MyComponent from './MyComponent';

test('renders correct ID using MemoryRouter', () => {
  render(
    <MemoryRouter initialEntries={['/user/456']}>
      <Routes>
        <Route path="/user/:id" element={<MyComponent />} />
      </Routes>
    </MemoryRouter>
  );

  expect(screen.getByText(/User ID: 456/i)).toBeInTheDocument();
});

This integration-style approach is less prone to breaking when you update react-router-dom and closely mirrors how your application runs in a real browser environment.