A client router may intercept links and update the visible view without reloading the document. It still needs a server route for direct visits, refreshes, and shared links. The History API changes the current history entry or pushes a new one, but it does not fetch content or make the server understand that path. A popstate handler must restore the view when the user moves backward or forward. Preserve actual anchors so keyboard interaction, open-in-new-tab, and script-free navigation remain available.
Browser Routing and History State
Working case
An analyst moves from the case list to case 47 and then presses Back. A client enhancement can fetch case 47, push its path after a successful render, and restore the list view on popstate. If the fetch fails, leave the original link navigation available. Opening the copied case path in a fresh tab must return the case page from the server. A route that works only after clicking from the homepage is incomplete, even if the in-app transition looks fast.
Implementation
const caseLink = document.querySelector("#open-case-47");
const casePanel = document.querySelector("#case-panel");
caseLink.addEventListener("click", async (event) => {
if (event.button !== 0 || event.metaKey || event.ctrlKey) return;
event.preventDefault();
try {
const response = await fetch(`/api${caseLink.pathname}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const caseRecord = await response.json();
casePanel.textContent = `${caseRecord.title}: ${caseRecord.status}`;
history.pushState({ caseId: 47 }, "", caseLink.pathname);
} catch { location.assign(caseLink.href); }
});
addEventListener("popstate", () => location.reload());Cost and boundaries
A history mutation is O(1) local work; rendering a fetched view costs O(B) for response size B and any DOM updates. Caching route data can save requests but needs invalidation after writes. Holding every previous page tree in memory can grow with navigation length; storing only small route state avoids that pressure. Test direct, refresh, Back, Forward, and failed-fetch paths. The code below shows a conservative path: if the enhanced load fails, the browser follows the original anchor.
Common Mistakes
- Do not replace real links with click-only spans.
- Do not push a path that the server cannot serve on refresh.
- Do not forget popstate after adding pushState.
Connected lessons
Frontend Application Architecture; Component Boundaries and State Ownership; Static, Server-Rendered, and Client-Rendered Pages; Locale and Time-Zone Boundaries; DOM events: enhance a working control without losing its baseline; HTML Tutorial; CSS Tutorial.
Failure trace
A click handler calls pushState before its case fetch finishes. The network fails; the address bar now says case 47 while the screen still shows the list. Reloading that URL returns a server 404 because the route exists only inside client code. This is a route contract failure, not a rendering glitch. Serve the path directly, update history after the new view is ready, and preserve an ordinary anchor as the fallback.
Verification
- Paste a case URL into a fresh tab and confirm the server returns useful HTML.
- Move Back and Forward twice after an in-page transition and compare both title and content with the URL.
- Force the API call to fail before navigation and confirm the anchor still reaches a valid page.
Decision note
A fast client transition is valuable only after direct navigation works. Keep route parameters in the URL and transient animation state in memory; storing whole page objects in history grows memory and creates stale copies.
Apply and check
Build Project: paginated inspection feed with safe writes; then check the boundary with Web Development: offline and delivery contracts quiz.
Further connections
Navigation, History, and Page Lifecycle; URL State and History Entry Contracts; Route Scroll and Focus Restoration.
