How to Test React Router Pages and Navigation

Use a memory router and RouterProvider to test React Router pages, links, and navigation without a real browser.

7 min read

React Router pages and navigation are tested with a memory router that keeps its history in memory. This guide shows how to test React Router rendering and link navigation with createMemoryRouter and RouterProvider, both exported from the react-router package in current React Router. Memory history makes the tests deterministic and independent of the browser URL bar.

Build a render helper

A helper creates a router from route objects, sets the starting URL, and renders it through RouterProvider.

App.jsxApp.jsx
// test/renderWithRouter.jsx
import { createMemoryRouter, RouterProvider } from "react-router";
import { render } from "@testing-library/react";
import Home from "../Home";
import About from "../About";
 
function renderWithRouter(initialPath = "/") {
  const router = createMemoryRouter(
    [{ path: "/", element: <Home /> }, { path: "/about", element: <About /> }],
    { initialEntries: [initialPath] }
  );
  return render(<RouterProvider router={router} />);
}

The memory router behaves like a real one but keeps its location in memory. initialEntries sets the URL the test starts on, so each test can jump straight to a route. The same helper can be reused by every route test in the suite.

Test that a route renders

The Home page is the root route, and it includes a link to the about page for the navigation test later.

App.jsxApp.jsx
// Home.jsx
import { Link } from "react-router";
 
export default function Home() {
  return (
    <div>
      <h1>Home</h1>
      <Link to="/about">About</Link>
    </div>
  );
}

A test can verify that a given path renders the matching page, without any clicking, by passing that path as the starting URL.

App.jsxApp.jsx
import { screen } from "@testing-library/react";
 
test("shows the home page at the root", () => {
  renderWithRouter("/");
 
  expect(screen.getByRole("heading", { name: "Home" })).toBeVisible();
});

The heading proves the route matched. If the route configuration is wrong, the heading does not render and the test fails.

Test navigation

A navigation test clicks the link and asserts that the destination page appears. It proves the link is wired to the right route, not just that the page exists.

App.jsxApp.jsx
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
 
test("navigates to the about page", async () => {
  const user = userEvent.setup();
  renderWithRouter("/");
 
  await user.click(screen.getByRole("link", { name: "About" }));
 
  expect(screen.getByRole("heading", { name: "About" })).toBeVisible();
});

The link behaves like a real anchor, updating the router history. The visible result is the about heading replacing the home heading. To read the raw path, expose the router from the helper and check router.state.location.pathname.

Test dynamic routes

Dynamic segments like an id come from the URL through useParams. Route a profile page at a path with a parameter and pass an initial path that includes the value, so the component reads it the same way it would in production.

App.jsxApp.jsx
test("renders the profile for a user id", () => {
  renderWithRouter("/users/42");
 
  expect(screen.getByRole("heading", { name: "User 42" })).toBeVisible();
});

The profile component reads the id with useParams and shows it in the heading. The test proves the parameter flows from URL to rendered output. For the routing concepts behind these components, see the React Router tutorial.

Common mistakes

Most route test failures come from the wrong starting URL or the wrong router type.

  • Using BrowserRouter in tests, where jsdom cannot fully update the URL bar.
  • Asserting router internals instead of the visible page content.
  • Forgetting initialEntries, so every test starts at the same default route.
  • Testing links in isolation instead of clicking them through the page.

What to learn next

Route tests share the runner setup from how to test React components with Vitest. Logic that lives in hooks can be tested separately and then verified through the page.

Rune AI

Rune AI

Key Insights

  • Create a memory router with createMemoryRouter and render RouterProvider.
  • Set the starting URL with initialEntries.
  • Reuse a renderWithRouter helper across route tests.
  • Click links with user-event and assert the new page content.
  • Read the current location from the router state when needed.
RunePowered by Rune AI

Frequently Asked Questions

Why use a memory router instead of BrowserRouter in tests?

A memory router keeps history in memory and lets you set the starting URL with initialEntries. It avoids real browser URL changes that jsdom does not fully support.

Which package exports createMemoryRouter?

The react-router package exports createMemoryRouter and RouterProvider. Versions before v7 split them into a separate react-router-dom package, which current React Router no longer needs.

Conclusion

Render React Router through a memory router, pass an initial path, click links with user-event, and assert the resulting page or location.