# createBrowserRouter vs createMemoryRouter: Key Differences in React Router

> Explore the key differences between createBrowserRouter and createMemoryRouter in React Router. Understand when to use each for DOM or testing environments.

- Repository: [Remix/react-router](https://github.com/remix-run/react-router)
- Tags: deep-dive
- Published: 2026-03-06

---

**Both `createBrowserRouter` and `createMemoryRouter` return fully-initialized DataRouter instances, but `createBrowserRouter` synchronizes with the browser's URL bar using the History API while `createMemoryRouter` maintains navigation state in a JavaScript array for testing and non-DOM environments.**

When building applications with `remix-run/react-router`, selecting the appropriate router factory determines how your application handles navigation state, URL synchronization, and server-side rendering hydration. While both functions instantiate the same underlying `createRouter` core, they diverge significantly in history implementation, environment requirements, and configuration options.

## History Implementation and Environment

The fundamental difference between these routers lies in how they manage the navigation stack.

### Browser History vs Memory History

`createBrowserRouter`, implemented in [`packages/react-router/lib/dom/lib.tsx`](https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/dom/lib.tsx), utilizes `createBrowserHistory` to interface with the browser's native `window.history` API. This enables **pushState** and **popstate** event handling, ensuring the URL bar reflects the current route and supports native browser navigation features like back/forward buttons.

In contrast, `createMemoryRouter`, defined in [`packages/react-router/lib/components.tsx`](https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/components.tsx), employs `createMemoryHistory` which stores navigation entries in a simple JavaScript array. This approach completely decouples routing from the URL bar, making it ideal for environments where `window` is undefined or inaccessible.

### Target Environments

**`createBrowserRouter`** is designed for:

- Standard browser applications requiring URL synchronization
- Server-side rendering (SSR) with client-side hydration
- Production web apps where SEO and shareable URLs matter

**`createMemoryRouter`** excels in:

- Unit and integration testing with deterministic navigation
- React Native applications without browser history
- Storybook and isolated component development
- Server-only rendering without DOM access

## Configuration Options and API Differences

Both routers accept common DataRouter options, but expose environment-specific configurations.

### createBrowserRouter Options

Located in [`packages/react-router/lib/dom/lib.tsx`](https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/dom/lib.tsx) (lines 645-663), `createBrowserRouter` accepts `DOMRouterOpts` including:

- **`basename`**: The base URL for all locations
- **`future`**: Future flags for v7 compatibility
- **`window`**: Custom window object injection for SSR environments
- **`hydrationData`**: Server-rendered state restored via `parseHydrationData()`

```typescript
const router = createBrowserRouter(routes, {
  basename: "/app",
  future: { v7_partialHydration: true },
  hydrationData: window.__INITIAL_DATA__
});

```

### createMemoryRouter Options

Found in [`packages/react-router/lib/components.tsx`](https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/components.tsx) (lines 311-330), `createMemoryRouter` accepts `MemoryRouterOpts`:

- **`initialEntries`**: Array of initial locations (defaults to `["/"]`)
- **`initialIndex`**: Starting index in the entries array

```typescript
const router = createMemoryRouter(routes, {
  initialEntries: ["/", "/dashboard", "/settings"],
  initialIndex: 1 // Start at /dashboard
});

```

## Server-Side Rendering and Hydration

The handling of server-rendered markup represents a critical functional divergence.

`createBrowserRouter` implements hydration support through `parseHydrationData()`, automatically detecting and restoring server-rendered loader data and error boundaries. This makes it the default choice for **Framework Mode** applications using React Router's data APIs with SSR.

`createMemoryRouter` does not process hydration data. When mounted, it initializes a fresh memory history stack based solely on `initialEntries`. This behavior is intentional for testing scenarios where you want complete control over the initial state without inheriting server context.

## Practical Implementation Examples

### Browser Router for Production Apps

For standard web applications requiring URL synchronization and SSR support:

```tsx
import { createBrowserRouter } from "react-router";
import { RouterProvider } from "react-router/dom";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />,
    children: [
      { index: true, element: <Home /> },
      { path: "about", element: <About /> }
    ]
  }
], {
  basename: "/app"
});

createRoot(document.getElementById("root")!).render(
  <RouterProvider router={router} />
);

```

### Memory Router for Testing

For deterministic testing without browser dependencies:

```tsx
import { createMemoryRouter } from "react-router";
import { RouterProvider } from "react-router/dom";
import { render, screen } from "@testing-library/react";

test("renders dashboard from initial entry", () => {
  const router = createMemoryRouter([
    { path: "/", element: <Home /> },
    { path: "/dashboard", element: <Dashboard /> }
  ], {
    initialEntries: ["/dashboard"]
  });

  render(<RouterProvider router={router} />);
  
  expect(screen.getByText("Dashboard")).toBeInTheDocument();
});

```

## Summary

- **History Management**: `createBrowserRouter` uses the browser's History API (`createBrowserHistory`) for URL synchronization, while `createMemoryRouter` uses an in-memory array (`createMemoryHistory`) with no URL changes.

- **Environment**: Use `createBrowserRouter` for production browser apps and SSR hydration; use `createMemoryRouter` for testing, React Native, and non-DOM environments.

- **Configuration**: `createBrowserRouter` accepts `basename`, `window`, and `hydrationData` for SSR; `createMemoryRouter` accepts `initialEntries` and `initialIndex` for deterministic state setup.

- **Implementation**: Both instantiate the same `createRouter` core but differ in history implementation—[`packages/react-router/lib/dom/lib.tsx`](https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/dom/lib.tsx) for browser, [`packages/react-router/lib/components.tsx`](https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/components.tsx) for memory.

## Frequently Asked Questions

### When should I use createMemoryRouter over createBrowserRouter?

Use `createMemoryRouter` when your application runs in an environment without a real browser URL bar, such as **unit tests**, **React Native** mobile apps, or **Node.js** server rendering without DOM access. It provides deterministic navigation control through `initialEntries`, making it ideal for testing specific route states without side effects.

### Does createMemoryRouter support server-side rendering?

While `createMemoryRouter` can run in server environments, it does **not** support hydration data parsing like `createBrowserRouter`. It initializes with a fresh memory history based on `initialEntries` rather than restoring server-rendered loader data. For SSR applications requiring state hydration from the server, `createBrowserRouter` with `parseHydrationData()` is the correct choice.

### Can I use createBrowserRouter in unit tests?

Technically yes, but it requires a **DOM environment** (like JSDOM) and will interact with the browser's History API, potentially causing side effects between tests. For isolated, deterministic tests, `createMemoryRouter` is strongly recommended because it avoids URL bar manipulation and allows precise control over the initial route history through `initialEntries` and `initialIndex`.

### What are the performance implications of each router?

Both routers share the same underlying `createRouter` core, so **routing logic performance is identical**. The difference lies in history management overhead: `createBrowserRouter` incurs the cost of native History API calls and URL bar updates, while `createMemoryRouter` performs simple array operations. In practice, this difference is negligible for application performance but significant for test isolation and environment compatibility.