Engrain by ByteRush

WEB.18.01 Mid-level· 13 minCore — asked often

Client-side routing: the History API, the Navigation API, and SPA vs MPA

How client-side routing works with pushState and popstate, hash routing, why deep links 404 without a server fallback, scroll and focus after navigation, the Navigation API, SPA vs MPA vs islands, prefetching and speculation rules.

Comes up in: Web fundamentals, Coding, Machine coding

Learn#

Lakshmi’s React app works perfectly until a user shares a link to /orders/42. The shared link shows the host’s 404 page, and the Back button scrolls to the wrong place.

Client-side routing changes the page without loading a new document. The History API does the URL part: history.pushState(state, "", "/orders/42") changes the address bar and adds a history entry, without any request. When the user presses Back or Forward, the browser fires popstate, and your router renders the view for the new URL. pushState itself never fires popstate.

The server must cooperate. Opening /orders/42 directly asks the server for that path; if it only knows index.html, it returns 404. Configure a fallback that serves index.html for app routes.

Hash routing (/#/orders/42) avoids that, because the part after # never reaches the server, but it gives uglier URLs and weaker SEO.

A client-side navigation and the Back buttonStep 1 of 4

State

  • URL: /orders

Page

    empty

    The user clicks a link to /orders/42. The router calls preventDefault, so the browser does not load a new page.

    Read the steps as text
    1. The user clicks a link to /orders/42. The router calls preventDefault, so the browser does not load a new page.
    2. history.pushState changes the URL to /orders/42 and adds a history entry. No request is sent, and popstate does not fire.
    3. The router matches /orders/:id with id = 42, renders that view, scrolls to the top and moves focus to the new heading.
    4. The user presses Back. Now popstate fires; the router reads location and renders /orders, restoring its scroll position.

    Predict#

    Predict first

    Your router calls history.pushState('', '', '/cart'). Does your popstate listener run?

    Interview angle#

    Say it like this — about 30 seconds

    “On a link click, the router prevents the default navigation, calls history.pushState with the new URL, and renders the matching view. Back and Forward fire popstate, which the router handles by rendering from location. Deep links 404 when the server has no fallback to index.html. After navigating, I restore or reset scroll, move focus to the new heading, and announce the page title. The Navigation API simplifies this by intercepting every navigation. MPAs with speculation rules or islands are often simpler and faster.”

    Stays on your device. Nothing is uploaded.
    Follow-up How does client-side routing work without page reloads?
    Intercept link clicks, update the URL with pushState, render the matching view, and handle popstate for Back and Forward.
    Follow-up Why do deep links 404 on some SPA deployments?
    The server looks for a file at that path; it needs a rewrite rule that serves index.html for app routes.
    Follow-up How should focus and scroll be handled after a client-side navigation?
    Scroll to the top for new pages and restore position on Back; move focus to the main heading or content, and update document.title.
    Follow-up SPA vs MPA: trade-offs?
    SPA: app-like transitions, client state kept, but more JavaScript. MPA: less JavaScript, better first load and SEO by default; view transitions and prefetching close the gap.

    Practice#

    Guided

    Write the matching core of a small router: patterns with parameters, query strings, trailing slashes and a catch-all.

    Done when

    • The first matching pattern wins; * catches everything else.
    • :name segments become decoded params; the query string becomes an object.
    • Trailing slashes do not change the match.
    Your solution
    Ctrl/⌘ + Enter

    Remember#

    Try to answer each card before you open it. Then add them to your reviews.

    Does pushState fire popstate?

    No. popstate fires for Back, Forward and history.go.

    Why do SPA deep links 404 without server config?

    The server has no file at that path; it must fall back to index.html.

    What should move after a client-side navigation?

    Focus, to the new page's heading or main content.

    What do speculation rules do?

    Tell the browser which pages to prefetch or prerender before the user clicks.

    Go deeper

    Four layers. Stop at the one your interview needs; each ends with a quick check.

    The core ideaEvery round
    Scroll restoration means putting the scroll position back on Back and Forward. Browsers try it automatically (history.scrollRestoration = "auto"), but an app that renders late may need to restore it itself.
    Check yourself: Where does the part after # go?

    Nowhere: the browser never sends it to the server.

    How it works underneathSenior rounds
    The Navigation API fires one navigate event for links, forms, Back and Forward; calling event.intercept({ handler }) turns it into a client-side navigation. Support: Chrome 102, Firefox 147, Safari 26.2 Source: MDN: Navigation API.
    Check yourself: What does navigation.addEventListener('navigate', …) let you do?

    Intercept every navigation, including link clicks and Back, in one place.

    Edge cases and trapsWhere people slip

    After a route change, set document.title, and focus the new <h1> (with tabindex="-1") or main region, so assistive technology announces the new page.
    Use real <a href> links so middle-click and “open in new tab” still work.

    Check yourself: Why move focus after a route change?

    Screen-reader and keyboard users otherwise get no sign that the page changed.

    In productionStaff-level answers
    Islands architecture, as in Astro, ships static pages with small interactive parts; MPAs reload documents but load less JavaScript.
    Speculation rules (<script type="speculationrules">) prefetch or prerender likely next pages: Chromium; Safari behind a setting Source: Chrome for Developers: Prerender pages.
    Check yourself: What is islands architecture?

    Mostly static HTML pages with small interactive components hydrated separately.

    Finished the five steps? Mark it, and its cards join your reviews.

    Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.

    Interview questions this answers

    • How does client-side routing work without page reloads?
    • Why do deep links 404 on some SPA deployments?
    • How should focus and scroll be handled after a client-side navigation?
    • SPA vs MPA: trade-offs?