So far, we've learned to build components, manage state, and update the screen. Most web applications also have several pages, and users need to move between them. Traditional websites fetch a new HTML document from the server for each navigation. A
single-page application (SPA)
built with a tool such as React handles navigation differently.
An SPA initially loads one HTML page. On later navigations, JavaScript updates what the user sees without requesting a whole new document. React Router is a widely used library for implementing this kind of
routing
. This guide focuses on React Router v6 and explains both how to use its features and how they work.
Basic Structure: BrowserRouter, Routes, and Route ๐
A basic setup uses three components.
BrowserRouter
This router uses the HTML5 History API to keep the URL and UI in sync. Wrap the application near the root. BrowserRouter uses the address bar to manage paths.
Routes
This container holds Route components. It finds the best match for the current URL and renders that route's element.
Route
This connects a path to the element to display at that path.
Entering / in the browser shows HomePage, and entering /about shows AboutPage.
The wildcard path="*" handles paths that don't match any of the other routes. It is commonly used for a 404 page.
, which lets JavaScript manipulate the session history stack.
pushState(state, title, url)
Adds an entry to session history and updates the address bar without reloading the document. This is what makes client-side navigation feel like moving between pages. Link uses it internally.
replaceState(state, title, url)
Replaces the current entry. Use it when the replaced entry should not remain available through the Back button.
popstate event
Fires when a history entry is activated using the browser's Back or Forward buttons or calls such as history.back() and history.forward(). BrowserRouter listens for the event and renders the components for the resulting URL.
BrowserRouter uses the History API to track location changes and render the UI for the current location.
React Router renders the selected route's element. With nested routes, it first renders the parent element. Outlet marks the place inside that element where the matched child element appears.
This gives you a way to render and organize complex layouts.
Call the navigate function returned by useNavigate. React Router uses the History API to update the URL and trigger rendering. It calls pushState by default or replaceState if you request it. Passing a path or a numeric history offset determines which history operation it uses.
As React Router matches a Route path against the current URL, it extracts the values of any dynamic segments. useParams exposes those values in an object your component can use.
returns a location object containing pathname, search, hash, and state. React Router updates it when the URL changes, so components using the hook receive the current location.
useSearchParams
parses and updates query strings. It reads location.search, uses the URLSearchParams API to work with parameters, and invokes navigation to update the URL when needed.
React Router finds the nested route branch that best matches the URL. For /dashboard/analytics, this example matches / (MainLayout), then dashboard (Outlet), then analytics (DashboardAnalytics). MainLayout renders, and nested Outlet components provide the places where the matched child elements appear, down to DashboardAnalytics.
A component wrapped with React.lazy loads asynchronously when its route first matches and the component is rendered. Suspense shows fallback UI while loading and renders the component once it is ready. This works with dynamic import() in tools such as Webpack or Vite, so the bundler can split code into smaller chunks and load them only when needed.
When the URL changes, React Router renders the component with the new parameters. useParams returns the new values. When a value in the dependency array ([userId]) changes, useEffect runs the effect again and fetches data for the new parameter.
We looked at how to use React Router v6 for navigation and how it works internally.
SPA routing uses the browser's
History API
to change the URL and update the UI without reloading the page.
React Router uses
URL matching
to find the route for the current path and render its component.
Link
calls pushState to update the URL.
useNavigate
lets you do so programmatically.
useParams
and
useLocation
make URL information available to components.
Nested routes
and
Outlet
organize layouts and determine where child route components render.
Code splitting
with React.lazy, Suspense, and dynamic imports can improve initial loading performance.
Understanding how React Router works helps you troubleshoot routing problems and design a more effective application structure.
Next time, we'll cover server-side rendering (SSR), static site generation (SSG), and hydration. We'll compare ways of delivering pages to users and look at how they affect performance and SEO.