React Router v7's Most Common Pitfalls, Fixed in One Demo
Routing bugs in React are disproportionately painful because they produce silent failures — a blank Outlet, an undefined param, a white flash on navigation — rather than clear error messages. This demo names those failures explicitly and shows the one-line fixes, saving the hours of debugging that every React developer eventually burns on the same three mistakes.
A complete SPA routing demo built with Vite, React 19, and react-router-dom v7 lays out the core APIs that trip up developers moving beyond their first tutorial. HashRouter keeps deployment zero-config by confining routing to the URL fragment, avoiding the server-side fallback rules BrowserRouter demands. Nested routes use a parent component with an <Outlet /> placeholder, and the demo calls out the exact mistake — placing Outlet in the route config instead of the component's return — that silently prevents child routes from rendering.
The demo also surfaces a parameter-name mismatch that returns undefined from useParams() when the route placeholder and the destructured variable don't match character-for-character. Lazy loading pairs React.lazy with a Suspense boundary wrapping the entire route tree, and the project includes a 404 page that combines path="*" fallback matching with a useNavigate timer to redirect after three seconds. The full source is available on Gitee, ready to clone and run.
The parameter-name mismatch between route definition and useParams destructuring is a class of bug that tooling should catch but doesn't — no linter warns when :productsId and productId diverge, yet the result is a silent undefined that looks like a logic error elsewhere.
HashRouter vs. BrowserRouter is often framed as an aesthetic choice, but the real dividing line is deployment infrastructure: teams without server access or CI/CD for nginx configs are effectively locked into hash-based routing regardless of preference.
The demo's structure — a single Suspense boundary wrapping the entire route tree — trades granular loading states for simplicity. Production apps typically need per-route Suspense boundaries to avoid blocking the navigation bar while a lazy-loaded page downloads.