Full-project lesson · needs Node.js, npm and real project files, so it does not run in the in-page playground.
React Router is the most widely used routing library for React. This lesson installs it and builds the first two pages. The code here was run in a real Vite project (React 19.3, React Router 8.4).
Requirements
React Router 8 requires Node.js 22.22 or newer and React 18 or newer. Check with node --version; see Node.js and npm for React.
Install
In your project folder:
npm install react-router
In versions 7 and 8, everything is imported from the single package react-router. Older tutorials import from react-router-dom. That package still exists for version 7 projects, but version 8 does not publish one, so new code uses react-router.
The three modes
React Router can be used in three ways, from simple to powerful:
| Mode | You use | Good for |
|---|---|---|
| Declarative | <BrowserRouter> with <Routes> and <Route> | Client-side apps that only need URL matching and navigation |
| Data | createBrowserRouter and <RouterProvider> | Adds loaders, actions and pending states to routes |
| Framework | A Vite plugin and route files | A full framework with server rendering and type-safe routes |
This course starts with declarative mode (the simplest), then shows data mode in Route Loaders and Lazy Routes. Framework mode is a separate setup, outside this course.
Step 1: wrap the app in BrowserRouter
Edit src/main.jsx so the whole app sits inside a router:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { BrowserRouter } from 'react-router'
import './index.css'
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>,
)
BrowserRouter listens to the address bar, makes the current location available to everything inside it, and handles Back and Forward.
Step 2: define routes
In src/App.jsx, describe which component belongs to which path:
import { Routes, Route, Link } from 'react-router'
function Home() {
return <h1>Home page</h1>
}
function About() {
return <h1>About page</h1>
}
function App() {
return (
<div>
<nav>
<Link to="/">Home</Link> | <Link to="/about">About</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</div>
)
}
export default App
What each piece does:
<Routes>looks at the current URL and renders the first matching<Route>.<Route path="/about" element={<About />} />says: when the path is/about, show<About />.<Link to="/about">renders a real<a href="/about">. Clicking it changes the URL without reloading the page.
In a browser test of this exact code, clicking About showed the About page and the address /about, Back returned to Home, and opening /about directly also worked (the test server returned index.html for that path, as a real host must).
Run it
npm run dev
Click the links and watch the address bar change while the page stays loaded. Press Back and Forward. Refresh on /about; the Vite dev server handles that for you.
Common mistakes
- Using
<a href>instead of<Link>for internal navigation. It works, but reloads the whole page and loses state. - Forgetting the router around the app. Components like
Linkthen throw “cannot be used outside a Router”. - Importing from
react-router-domin a version 8 project. Import fromreact-router. - Forgetting the server fallback in production, which causes 404 errors on refresh (Deployment Fundamentals).
Key points
npm install react-router, import fromreact-router.- Wrap the app in
<BrowserRouter>; define<Routes>and<Route path element>. - Use
<Link to>for internal links.