React Router BrowserRouter: HTML5 History API & Setup
The <BrowserRouter> component is the foundation of client-side routing in React. It uses the HTML5 History API to manipulate the browser's URL and history stack without reloading the page. Understanding how BrowserRouter works and how to configure it is essential for building modern single-page applications with React Router.
What is BrowserRouter?
BrowserRouter is a React component that wraps your entire application and enables client-side routing using the HTML5 History API. When you navigate to a new route, BrowserRouter updates the URL in the address bar and adds a new entry to the browser's history stack—without making an HTTP request to the server. This creates the seamless, fast navigation experience users expect from modern web applications.
Unlike traditional server-side routing where each URL change triggers a server request and page reload, BrowserRouter keeps your React component tree in memory and renders only the components that match the current URL. This is the defining characteristic of a single-page application (SPA).
How BrowserRouter Uses the History API
The HTML5 History API provides two critical methods that BrowserRouter relies on:
pushState(state, unused, url)— Adds a new entry to the history stack and changes the URL without reloading the page.replaceState(state, unused, url)— Modifies the current history entry's URL without creating a new entry.popstateevent — Fires when the user clicks the browser's back/forward buttons.
When you navigate using React Router (e.g., <Link to="/about">), BrowserRouter calls pushState() to update the URL. The component tree re-renders to match the new URL, but no server request occurs. When the user clicks the back button, the popstate event fires, BrowserRouter detects the URL change, and React re-renders the appropriate components.
This decoupling of URL and server requests is what makes SPAs possible.
Configuring BrowserRouter: Key Props
The basename Prop: Apps Not at Root
If your application is not served from the domain root (e.g., it's deployed at https://example.com/my-app/ instead of https://example.com/), use the basename prop:
import { BrowserRouter } from 'react-router-dom';
import App from './App';
export default function Root() {
return (
<BrowserRouter basename="/my-app">
<App />
</BrowserRouter>
);
}
Now all routes are relative to /my-app. A <Link to="/about"> navigates to /my-app/about, and the URL reflects this correctly. This is essential when hosting multiple applications on one domain or serving an app from a subdirectory.
The children Prop: Your App Component
The children prop is simply the rest of your application:
<BrowserRouter>
<App /> {/* This is the children prop */}
</BrowserRouter>
Every component inside <BrowserRouter> can access routing features via hooks like useLocation, useNavigate, and useParams.
The window Prop: Advanced Use Cases
The window prop lets you provide a custom window object. This is rarely needed and is primarily for testing or working with unusual environments (like Electron apps or iframes). Unless you have a specific reason, ignore this prop.
Server-Side Configuration: The Critical Step
Here's where many developers get stuck: because BrowserRouter uses the History API to create clean URLs (e.g., /about, /products/123), your server must be configured to handle these URLs correctly.
The Problem
When a user bookmarks or directly navigates to https://example.com/about, the browser makes an HTTP GET request to the server for the /about path. If your server is not configured to handle this, it returns a 404 Not Found error because there is no actual /about file on the server.
The Solution: Fallback to index.html
You must configure your server to serve your main index.html file for any request that is not a static asset (CSS, JavaScript, image files). This allows your React application to load, detect the current URL from the History API, and render the correct components.
Create React App (Development):
Create React App's development server is already configured for this. No action needed.
Express (Node.js):
Use the connect-history-api-fallback middleware:
import express from 'express';
import history from 'connect-history-api-fallback';
import path from 'path';
const app = express();
// Serve static files (CSS, JS, images)
app.use(express.static(path.join(__dirname, 'build')));
// Fallback: serve index.html for all non-static requests
app.use(history());
app.listen(3000, () => console.log('Server on port 3000'));
Or use a catch-all route:
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
Netlify (static hosting):
Create a _redirects file in your public directory:
/* /index.html 200
This tells Netlify to serve index.html for all URLs, preserving the clean URL in the address bar.
Vercel (static hosting):
Create a vercel.json configuration:
{
"rewrites": [
{ "source": "/(.*)", "destination": "/index.html" }
]
}
Nginx:
Use a try_files directive:
location / {
try_files $uri /index.html;
}
The pattern is identical across all platforms: serve index.html for requests that don't match static files. This ensures your React app always loads, and React Router can read the URL from the History API and render the correct components.
Common Configuration Patterns
Multi-Environment Setup
const basename = process.env.NODE_ENV === 'production'
? '/my-app'
: '/';
export default function Root() {
return (
<BrowserRouter basename={basename}>
<App />
</BrowserRouter>
);
}
Detecting the Current Location
Inside any component wrapped by BrowserRouter, use the useLocation hook:
import { useLocation } from 'react-router-dom';
function MyComponent() {
const location = useLocation();
console.log(location.pathname); // Current URL path
console.log(location.search); // Query string
console.log(location.hash); // Hash fragment
}
Key Takeaways
- BrowserRouter Uses History API: It manipulates the URL and history stack without page reloads, enabling fast client-side navigation.
- basename for Subdirectories: If your app is not at the domain root, set
basenameto prepend a path to all routes. - Server Configuration is Required: Always configure your server to serve
index.htmlfor non-static requests. This is non-negotiable forBrowserRouterto work correctly. - Clean URLs, No File Structure: Users see clean URLs (e.g.,
/about) that don't correspond to actual files on the server. - Fallback Everywhere: Every server platform (Express, Netlify, Vercel, Nginx, etc.) has a simple configuration to enable this fallback. Check your platform's documentation.
Frequently Asked Questions
Why does my React Router app return 404 when I refresh a page?
Your server is not configured to fall back to index.html. Every URL that doesn't match a static file should serve index.html, allowing React Router to take over and render the correct component.
Do I need to configure anything if I'm using Create React App?
No. Create React App's development server and production build process are pre-configured to handle this. If you eject from Create React App, you'll need to add the fallback yourself.
What's the difference between <BrowserRouter> and <HashRouter>?
<BrowserRouter> uses the History API for clean URLs (/about). <HashRouter> uses URL hashes (/#/about). <BrowserRouter> is preferred because it produces cleaner URLs and better SEO. <HashRouter> requires no server configuration but is rarely used in modern applications.
Can I have multiple <BrowserRouter> components?
No. You should have only one <BrowserRouter> at the top level of your app. Wrapping nested components in additional <BrowserRouter> components will cause routing to malfunction.
What if my app is in an iframe or Electron window?
The window prop lets you provide a custom window object, but this is an advanced use case. Most apps can ignore this prop.