MDN History API - discussion (2023-01-05)
MDN's History API docs are a good refresher for what browsers actually provide under every router abstraction.
If you're building custom navigation behavior (modals as routes, stacked panels), how often do you touch history directly? Do you treat history state as part of your route contract, or do you keep it as an internal implementation detail? What's your strategy for keeping back/forward behavior predictable when the UI has internal 'modes'?
Comments (10)
Back to latestWe only touch history directly when we need something truly custom (like stacked overlays).
Otherwise we keep it behind routing primitives so it doesn't leak everywhere.
Back/forward gets weird when internal modes don't show up in the URL. If the mode matters, it should be part of the navigation story somehow.
Related: Routing and Navigation.
History state as contract is useful for debugging. If a bug report includes a history state snapshot, you can replay weird flows.
But if you store huge objects, you'll regret it.
Predictability rule: if the user can see it, back should undo it. If they can't see it, back shouldn't care.
It sounds obvious, but it forces you to be honest about what 'mode' means.
We touched history directly for one thing: stacked overlays that needed real back/forward semantics.
ts
history.pushState({ overlay: 'filters' }, '', '?overlay=filters');
window.addEventListener('popstate', (e) => {
// reconcile overlay mode from e.state / location.search
});
Once we did that, we had to treat overlay mode as a contract, not just UI state.
Counterpoint: direct history work is where bugs go to hide.
If you're going to do it, I would keep it behind one routing primitive and render a marker when it is active.
Yep. We ended up with a single useHistoryBridge() and a route-visible flag so support could tell when the bridge was involved.
Otherwise the bug reports are impossible to reason about because back/forward behavior depends on hidden state.
What helped us: decide what counts as "navigation" and encode it in the URL, even if it's just a small query key.
Once we stopped pretending internal modes were not navigation, back/forward became much less surprising.
A tiny pattern that kept things predictable: keep history state small and serializable.
We store only a couple of keys (overlay, step, scrollHint), and everything else is derived from the URL + route doc.
A misunderstanding I see: people think history state is a convenient store for random objects.
If you treat it as a contract surface, you naturally keep it small and make the UI reproducible.
If you adopt the forum docs posture, the goal is: a screenshot + URL + (maybe) a tiny history state payload should explain the whole navigation story.
Anything beyond that tends to rot.