By WebNest Studio Engineering Team
React Tutorial
Client-Side Routing with React Router
Single-page React applications render everything from JavaScript, so the browser's native URL-based navigation doesn't automatically map to different "pages." React Router (the react-router-dom package) solves this by letting you define routes that map URL paths to components, all rendered client-side without a full page reload.
React Router v6+ centers on a small set of components and hooks: BrowserRouter to enable routing, Routes and Route to declare which component renders for which path, Link and NavLink for navigation, and hooks like useParams and useNavigate to read route data and navigate programmatically.
Why it matters
A React application is a single HTML page, yet users expect addresses they can bookmark, a working back button and links they can share. A router maps the URL to the component that should be shown, without reloading the page. React Router is the standard library for it.
Setting Up Routes
The whole app is wrapped in a <BrowserRouter>, which uses the HTML5 history API to keep the UI in sync with the URL. Inside it, a <Routes> element contains one <Route> per path, each pairing a path with an element to render. Only the first matching route renders its element, and a path of "*" is commonly used as a catch-all "not found" route.
Navigating Between Routes
Use <Link to="/about"> instead of a plain <a href="/about"> for in-app navigation — Link intercepts the click and updates the URL without a full page reload, preserving application state. NavLink behaves the same way but also applies an "active" styling class automatically when its path matches the current URL, which is useful for navigation menus.
Reading URL Parameters and Navigating Programmatically
Dynamic segments in a path, like /users/:id, are read inside the matching component with the useParams() hook, which returns an object such as { id: "42" }. For navigation that must happen in response to logic rather than a direct click — after a form submits successfully, for example — the useNavigate() hook returns a function you call with a path, such as navigate("/dashboard") or navigate(-1) to go back.
useParams()— reads dynamic segments like:idfrom the current URL.useNavigate()— returns a function to navigate imperatively, e.g. after an async action completes.useLocation()— gives access to the current URL, search params, and any navigation state.
Example
A small app with static routes, a dynamic route, and programmatic navigation
import { BrowserRouter, Routes, Route, Link, useParams, useNavigate } from 'react-router-dom';
function Home() {
return <h1>Welcome Home</h1>;
}
function UserProfile() {
const { id } = useParams();
const navigate = useNavigate();
return (
<div>
<h1>User Profile #{id}</h1>
<button onClick={() => navigate('/')}>Back to Home</button>
</div>
);
}
function NotFound() {
return <h1>404 — Page Not Found</h1>;
}
export default function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link> | <Link to="/users/42">User 42</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users/:id" element={<UserProfile />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
A nav bar with two links is always visible. Clicking "User 42" changes the URL to /users/42 and renders "User Profile #42" with a "Back to Home" button; clicking that button calls navigate("/") and returns to the Home view — all without a full page reload.
Common Mistakes
- Using a plain <a> tag for internal navigation, which causes a full page reload and loses all in-memory React state.
- Forgetting that route matching order and specificity matter — placing a catch-all "*" route before more specific routes can shadow them.
- Reading useParams() outside of a component actually rendered by a matching Route, where the params will be undefined.
- Calling useNavigate() at the top level of a module instead of inside a component or hook, where hooks are not valid.
Exercise
Do this in a Vite + React project after installing the router with npm install react-router-dom. In src/App.jsx, set up three routes: / shows a Home component, /about shows an About component, and /users/:id shows a User component that displays the id from the URL. Add navigation links to Home and About.
Expected result: clicking the links changes the URL and the content without a page reload, and visiting /users/42 shows "User 42".
import { BrowserRouter, Link, Route, Routes, useParams } from "react-router-dom";
function Home() {
return <h1>Home</h1>;
}
function About() {
return <h1>About</h1>;
}
function User() {
// TODO: read the id from the URL
return <h1>User</h1>;
}
export default function App() {
return (
<BrowserRouter>
{/* TODO: a nav with links to "/" and "/about" */}
{/* TODO: the three routes */}
</BrowserRouter>
);
}
Show hints
- A route is written
<Route path="/about" element={<About />} />, inside<Routes>. useParams()returns an object holding the dynamic parts of the URL, such asid.
Show solution
import { BrowserRouter, Link, Route, Routes, useParams } from "react-router-dom";
function Home() {
return <h1>Home</h1>;
}
function About() {
return <h1>About</h1>;
}
function User() {
const { id } = useParams();
return <h1>User {id}</h1>;
}
export default function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link> <Link to="/about">About</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users/:id" element={<User />} />
</Routes>
</BrowserRouter>
);
}
Quick quiz
1. Why use <Link to="/about"> instead of <a href="/about"> for internal navigation?
- A. Link is styled automatically
- B. An a tag does not work in React
- C. Link changes the URL without reloading the page, so the application keeps its state
- D. Link is required by HTML
Show answer
C. A plain anchor makes the browser request the page again from the server.
2. Which hook reads a dynamic segment such as :id from the URL?
- A. useParams
- B. useState
- C. useRoute
- D. useLocation
Show answer
A. useLocation gives the whole current location, and useSearchParams reads the query string.
3. Which hook navigates from code, for example after a form has been submitted?
- A. useHistory in every version
- B. useNavigate
- C. useLink
- D. useRedirect
Show answer
B. It returns a function: navigate("/dashboard").
Interview questions
What is client-side routing?
Show answer
In a traditional site, each link makes the browser request a new HTML page from the server. With client-side routing, the application loads once; clicking a link changes the URL through the browser's History API and the router renders the matching component, with no full page load. Navigation is faster and state is kept, but the server must be configured to return the application's index.html for every route, so that a direct visit or a refresh still works.
How do you protect a route so that only signed-in users can see it?
Show answer
Wrap the protected routes in a component that checks the authentication state. If the user is signed in, it renders the child route; otherwise it redirects to the login page with Navigate, remembering the address so that the user can be returned there afterwards. This is a convenience for the interface only: the server must still check authorisation on every API request, because client-side checks can be bypassed.
Key Points to Remember
- BrowserRouter, Routes, and Route wire URL paths to components without full page reloads.
- Link and NavLink handle in-app navigation; NavLink also supports "active" styling.
- useParams() reads dynamic URL segments; useNavigate() navigates programmatically from event handlers or effects.
- A "*" catch-all Route provides a not-found page, and should generally be listed last.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Shows a live preview of plain HTML, CSS and JavaScript. JSX needs a React project on your computer. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
These examples need a project environment on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering