React / React Router / Router Types
Home /React /React Router /Router Types

Router Types

Router Types

React Router ships multiple router implementations. Each is optimised for a different runtime environment or use case. Choosing the right one matters.

BrowserRouter

Uses the HTML5 History API (pushState, replaceState). URLs look clean: /about. Requires server-side fallback config to handle direct URL hits.

How it works

BrowserRouter wraps the app in a context that listens to window.history events. When the user clicks a <Link>, it calls history.pushState(), updating the URL without a page load, then re-renders the matched <Route> components.

Server config required: If a user visits example.com/about directly, the server must return your index.html for every route (not a 404). Configure your Nginx/Apache/Vite dev server accordingly.

HashRouter

Uses the URL hash fragment: /#/about. Works without any server config because the hash is never sent to the server. Good for static file hosting.

main.jsx: HashRouter

import { HashRouter } from 'react-router-dom';// URL will look like;http://example.com/#/about// The server only ever sees: http://example.com/createRoot(root).render(
  <HashRouter><App /></HashRouter>
);

MemoryRouter

Keeps the navigation state in memory, no URL changes at all. Perfect for tests, React Native, Storybook, or embedded widgets.

Component.test.jsx: MemoryRouter in tests

import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';

it('renders profile page', () => {
  render(
    <MemoryRouter initialEntries={['/profile/7']}>
      <App />
    </MemoryRouter>
  );
  // assert profile renders for id=7
});

StaticRouter

Used on the server during Server-Side Rendering (SSR). It never changes location  you pass the current URL as a prop from the server request.

server.js: Express + StaticRouter

import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom/server';

app.get('*', (req, res) => {
  const html = renderToString(
    <StaticRouter location={req.url}>
      <App />
    </StaticRouter>
  );
  res.send(`<!DOCTYPE html><html><body>${html}</body></html>`);
});

RouterProvider

The v6.4+ data router API. Pairs with createBrowserRouter. Enables loaders, actions, deferred data, and error boundaries at the route level.

The RouterProvider + createBrowserRouter pattern is now the recommended approach. It unlocks loader functions that fetch data before rendering, action functions that handle form mutations, and route-level error boundaries.

main.jsx: RouterProvider (recommended)

import {
  createBrowserRouter,
  RouterProvider,
} from 'react-router-dom';
import Home from './pages/Home';
import Profile, { profileLoader } from './pages/Profile';

const router = createBrowserRouter([
  { path: '/',          element: <Home /> },
  {
    path: '/profile/:id',
    element: <Profile />,
    loader: profileLoader,  // fetches data BEFORE render
    errorElement: <ErrorPage />,
  },
]);

createRoot(root).render(
  <RouterProvider router={router} />
);

Profile.jsx — using a loader

import { useLoaderData } from 'react-router-dom';

// Runs on the router, before the component mounts
export async function profileLoader({ params }) {
  const res = await fetch(`/api/users/${params.id}`);
  if (!res.ok) throw new Response('Not Found', { status: 404 });
  return res.json();
}

export default function Profile() {
  const user = useLoaderData();
  return <h1>{user.name}</h1>;
}

NativeRouter

Designed for React Native apps. Uses the native navigation history without depending on the browser DOM or History API.

references: