In short
In apps like Gmail, clicking a link changes the URL but the page never fully reloads. The History API makes that possible, and keeps the back button working.
`history.pushState`/`replaceState` and the `popstate` event let JavaScript manipulate the browser's URL and back/forward history without a full page reload — the foundation of client-side routing. This enables single-page applications to keep URLs meaningful and navigable while updating views without performing a full document navigation.

Think of it like this
Pages in a train ticket booklet
Each stop in your trip gets a page in the booklet. You can add a new page without starting a new booklet, and flipping back shows earlier stops. pushState adds a page, the back button flips back.
Watch it happen
How a single-page app changes view
clicks /about link
1/6Your click handler calls preventDefault, so the browser doesn't load a new page.
The big ideas
What you need to know
pushState adds an entry
history.pushState(state, "", url) changes the URL and adds a history entry, without loading a new page.
replaceState swaps it
Updates the current URL without adding an entry. Good for filters, so back doesn't step through every click.
popstate on back
When the user presses back or forward, popstate fires. Your code re-renders the right view.
You render, not the browser
pushState only changes the URL. Showing the right content is your job.
See it in code
Walk through the code
1function render(path) {2 app.innerHTML = path === "/about" ? "<h1>About</h1>" : "<h1>Home</h1>";3}4 5document.addEventListener("click", (e) => {6 const link = e.target.closest("a[data-link]");7 if (!link) return;8 e.preventDefault();9 history.pushState({}, "", link.href);10 render(location.pathname);11});12 13window.addEventListener("popstate", () => render(location.pathname));14render(location.pathname);1/4render decides what to show for a path. Here, just two views.
This is a tiny router. React Router and the Next.js router do the same thing under the hood, with many more features.
Save yourself the pain
Common mistakes
Oops
Expecting popstate to fire when you call pushState.
Do this instead
popstate fires only on back and forward. Render the view yourself right after pushState.
Oops
Refreshing /about on your deployed app and getting a 404.
Do this instead
Configure the server to send index.html for all app routes, so your JavaScript can render them.
Your mission
Build a tiny router
Quick check
Your app calls history.pushState({}, "", "/profile"). What changes on screen by itself?
Go deeper
- 1Use `pushState` to change the URL without reloading the page. Verify the behavior with realistic data and handle the failure or empty state as well.
- 2Listen for `popstate` to react to the browser's back/forward buttons. Validate your understanding by applying it in a small practical example.
- 3Understand this is exactly what React Router/Next.js use under the hood.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.