Full-project lesson · needs Node.js, npm and real project files, so it does not run in the in-page playground.
There are four ways to move around a React Router app. Choose by what triggers the move.
| Tool | Triggered by | Use for |
|---|---|---|
<Link> | The user clicks a link | Normal navigation |
<NavLink> | The user clicks a link; it knows if it is active | Menus and tabs |
useNavigate() | Code (after a form, a timer, a login) | Programmatic navigation |
<Navigate> | Rendering | Redirects |
Link
<Link to="/about">About us</Link>
It renders <a href="/about">, so it supports opening in a new tab, copying the address and keyboard use. Never wrap a <button> inside a link or vice versa.
Relative links. A to without a leading slash is relative to the current route: from /products, <Link to="42"> goes to /products/42, and <Link to=".."> goes up one level.
NavLink: styling the current page
NavLink is a Link that knows whether it matches the current URL. It adds the class active and the attribute aria-current="page" automatically, which helps screen reader users. Style it with CSS (a.active { ... }) or with a function:
import { Routes, Route, NavLink, Navigate, useNavigate } from 'react-router'
function Home() {
const navigate = useNavigate()
return (
<div>
<h1>Home page</h1>
<button onClick={() => navigate('/about')}>Go to About with a button</button>
</div>
)
}
function About() {
const navigate = useNavigate()
return (
<div>
<h1>About page</h1>
<button onClick={() => navigate('/')}>Go home with a button</button>
</div>
)
}
const linkStyle = ({ isActive }) => ({
fontWeight: isActive ? 700 : 400,
textDecoration: isActive ? 'underline' : 'none',
})
function App() {
return (
<div>
<nav style={{ display: 'flex', gap: 16 }}>
<NavLink to="/" end style={linkStyle}>Home</NavLink>
<NavLink to="/about" style={linkStyle}>About</NavLink>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/old-about" element={<Navigate to="/about" replace />} />
</Routes>
</div>
)
}
export default App
This project was built with Vite and tested in a browser: the active link has aria-current="page" and bold text, the buttons navigate, and /old-about redirects to /about.
Details:
endon the Home link stops it from being “active” for every path that starts with/(all of them). Useendon links to/and on parent links when you want an exact match.style,classNameandchildrenall accept a function that receives{ isActive, isPending }.aria-current="page"is added for you; do not remove it.
useNavigate: navigate from code
Use it after an event that is not a plain link click: a successful login, a form submission, a “Back” button:
const navigate = useNavigate()
async function handleSubmit(e) {
e.preventDefault()
await saveOrder()
navigate('/thank-you') // go to a new page
}
navigate(-1) // like the browser Back button
navigate('/login', { replace: true }) // replace the current history entry
replace: true swaps the current entry instead of adding one, so the user cannot press Back to a page that should be skipped, such as a login form after logging in.
You can also pass state that does not appear in the URL: navigate('/next', { state: { fromSearch: true } }), read with useLocation().state. It is lost on refresh in some cases, so do not rely on it for important data.
Navigate: redirect while rendering
<Navigate to="/about" replace /> redirects as soon as it renders. Use it for old URLs (as above) or for protected pages:
function RequireAuth({ user, children }) {
if (!user) return <Navigate to="/login" replace />
return children
}
(Real authentication also needs server checks; see API Security Fundamentals.)
Use real links for navigation
If a click only goes to another page, use Link, not a button with navigate. A link works with middle-click, “open in new tab”, keyboard and assistive technology. Reserve navigate() for navigation that follows an action.
Active section highlighting
By default, NavLink to="/products" is active for /products and also for /products/42, because the second path is “inside” the first. That is usually what you want for a menu. Use end if you do not.
Scroll restoration and focus
When the route changes, the browser does not scroll to the top or move focus by itself in a single-page app. In data mode, add <ScrollRestoration /> to your layout. For keyboard and screen reader users, move focus to the new page’s main heading after navigation; see Accessible Focus States and Accessibility in React.
Key points
Linkfor clicks,NavLinkfor menus (addsactiveandaria-current),useNavigatefor navigation from code,Navigatefor redirects.- Use
endso/links are not always active. - Prefer real links; manage scroll and focus after navigation.