跪拜 Guibai
← All articles
Frontend · React.js

Frontend Routing Is Three Objects Tricking the Browser

By 绿色蟑螂 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Understanding routing as a browser con rather than real navigation removes the magic from React Router's API surface. Developers who internalize the navigator-location-history model stop memorizing hooks and start reasoning about navigation state directly, which eliminates a common source of bugs in auth flows and nested layouts.

Summary

SPAs serve a single index.html and let JavaScript take over. The URL stops being a resource request and becomes a frontend state label — a signal for which component tree to render. Hash-based routing exploits the anchor's no-refresh behavior, while the History API uses pushState and replaceState to silently rewrite the address bar. Both are an optical illusion: the browser thinks navigation happened, but no HTTP request was sent.

React Router wraps this illusion into navigator (Link, useNavigate, Navigate), location (useLocation, with a hidden state field for cross-page data), and history (the stack that push and replace operate on). Auth-guarded routes chain all three: intercept an unauthenticated visit, navigate to login while stashing the original path in location.state, then navigate back with replace after login succeeds.

Dynamic route matching follows declaration order, not specificity, so concrete paths must precede parameterized ones. Nested routes render into an Outlet slot, building a component tree from a URL pattern. BrowserRouter produces clean, semantic URLs and should be the default unless the hosting environment can't support it.

Takeaways
SPAs serve one HTML file; the URL becomes a frontend state label, not a backend resource request.
Hash routing exploits the anchor's no-refresh design; BrowserRouter uses the History API's pushState and replaceState to silently rewrite the address bar.
React Router's entire API is syntactic sugar over three objects: navigator (where to go), location (where you are), and history (the stack you can go back through).
location.state carries hidden cross-page data invisible in the URL, ideal for remembering where a user came from before an auth redirect.
Use replace instead of push when navigating away from login pages so the back button doesn't return users to a completed login form.
Route matching follows declaration order, not specificity — concrete paths must be written before parameterized ones to avoid swallowing literal segments as params.
Prefer BrowserRouter over HashRouter for clean, semantic URLs unless the hosting environment cannot support server-side fallback configuration.
Conclusions

The mental model shift from 'URL as resource locator' to 'URL as frontend state label' is the single unlock that makes React Router's API feel inevitable rather than arbitrary.

Framing frontend routing as a deliberate browser con — not a feature — strips away the illusion of real navigation and clarifies why every API exists: to manage a stack of fake page transitions.

location.state is an underused mechanism that avoids polluting query strings with internal routing metadata, yet many codebases still stuff redirect targets into ?redirect= params out of habit.

React Router v6's claimed automatic route sorting is a leaky abstraction; the safe, explicit practice of ordering concrete paths first remains the more reliable default.

Concepts & terms
Hash routing
Uses the URL fragment after # to simulate navigation. Changing the hash does not trigger a browser refresh, and the frontend listens for hashchange events to swap components.
History API (pushState / replaceState)
HTML5 methods that let JavaScript change the address bar URL without triggering a page reload. pushState adds a new history entry; replaceState overwrites the current one.
location.state
A hidden payload attached to a navigation that persists across route changes but never appears in the URL. Commonly used to pass a redirect target through an auth flow.
Outlet
A placeholder component in React Router that renders matched child routes, analogous to Vue's <router-view>. It enables nested layouts where parent and child components render together.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗