Contents

Frontend Development › JavaScript & TypeScript

History API

Changing the URL without reloading; the basis of client-side routing.

Also known as: history api, pushstate, client-side routing

The History API lets scripts change the URL without navigation: pushState() adds an entry, replaceState() edits in place, and popstate fires on back/forward — the foundation of client-side routing. SPAs update views and URLs independently, keeping shareable addresses without server round trips.

history.pushState({page: 2}, '', '/items?page=2');  // URL changes, no reload
window.addEventListener('popstate', render);        // back/forward renders

The URL stays meaningful (bookmarkable, shareable, server-renderable in principle) while navigation feels instant. The server must cooperate — deep links need serving the app shell — or refreshes 404.

The classic mistakes:

  • pushState without server fallback. Client routes that 404 on refresh or for crawlers break sharing and SEO. Serve the shell (or pre-render) for app routes.
  • Breaking back/forward. Hijacking clicks without pushing state (or pushing duplicates per keystroke) makes back skip or trap. Every view change that matters gets exactly one entry.
  • No scroll restoration. Client navigation that leaves scroll mid-page disorients. Manage scroll per route (top on new views, restore on back).
  • Ignoring focus. Route changes must move focus and announce (screen readers don’t know the “page” changed). Treat routing as navigation for assistive tech too.
  • State in history objects. Stuffing large state into pushState bloats memory and breaks on reload. URLs carry address; stores carry state.
  • Hash-routing by default. Hash fragments (#/page) dodge server config but break anchors, SEO and server rendering. Prefer real paths with proper fallback.
  • Forgetting canonical URLs. Client routes need canonical tags or search indexes fragment. Coordinate with canonical URL discipline.

How to use it: pushState per meaningful view, server fallback for deep links, scroll and focus managed per navigation, URLs kept shareable. The address bar is a UI — route like it matters.