跪拜 Guibai
← All articles
Frontend

React Router v7 in Practice: Lazy Loading, Nested Routes, and the SPA Routing Playbook

By 东风破_ ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

React Router v7 is the current routing standard for new React projects, and the patterns here — lazy boundaries, nested layouts, hash vs. history mode tradeoffs — are the same decisions every SPA team faces at project start. Getting them right from the first route definition avoids costly refactors later.

Summary

A full demo application walks through the entire React Router v7 API surface using React 19 and Vite. The project configures HashRouter for zero-config deployment, then layers in lazy-loaded page chunks behind Suspense boundaries to keep initial bundle sizes small. Nested routes under /products show how parent layouts persist while child routes swap content through Outlet, and dynamic segments like /user/:id feed parameters into components via useParams.

Navigation uses Link components instead of raw anchor tags to avoid full-page reloads, while useNavigate handles programmatic redirects — a 404 page counts down three seconds before sending users home. A wildcard path="*" route catches all unmatched URLs, and the Navigate component with replace demonstrates clean URL migration from old paths to new ones.

The piece closes by mapping every React Router abstraction back to the native hashchange event, making the case that the framework is just a component-shaped wrapper around browser primitives.

Takeaways
HashRouter requires no server configuration and works purely on the front end; BrowserRouter produces cleaner URLs but demands server-side fallback rules to prevent 404s on refresh.
Wrapping page imports in lazy() and placing them inside a Suspense boundary splits the bundle so users download only the code for the route they actually visit.
Dynamic segments like :id feed values into components through the useParams hook, keeping routing state out of the prop-drilling chain.
Nested routes render child content into a parent's Outlet slot, so shared layout chrome stays mounted while only the inner area swaps.
useNavigate triggers SPA-style transitions in code without a full page reload, unlike setting window.location.href.
The Navigate component with the replace prop swaps the current history entry instead of pushing a new one, preventing broken back-button behavior after redirects.
A wildcard path="*" route placed last in the Routes array catches every unmatched URL and serves as the 404 fallback.
Conclusions

Mapping React Router's API back to the raw hashchange event reveals how thin the abstraction really is — the framework adds component ergonomics but no novel browser capability.

The HashRouter vs. BrowserRouter decision is often framed as a deployment concern, but it also shapes the entire URL design of an application before a single server is configured.

Lazy loading is treated as an optimization, but in a Router context it doubles as a code-organization pattern: each route boundary becomes a natural split point for teams working on separate pages.

Concepts & terms
HashRouter
A React Router container that manages navigation using the URL hash fragment (e.g., #/about). It listens to hashchange events and requires no server-side configuration, making it suitable for static deployments.
Outlet
A React Router component that acts as a placeholder inside a parent route's component. Child route elements render into the nearest Outlet, enabling nested layouts where the parent's chrome stays mounted.
lazy + Suspense
React's code-splitting mechanism. lazy() dynamically imports a component only when it is first rendered, and Suspense defines a fallback UI shown during that asynchronous load.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗