History
tss-router ships with two History implementations. They share the same interface and you swap them at the application root.
Browser history
import {createBrowserHistory} from 'tss-route-lib';
// `RouteProvider` comes from `routingHooksFactory(router)` — see Getting Started.
const history = createBrowserHistory();
<RouteProvider history={history}>
<App />
</RouteProvider>;- Reads
window.locationandwindow.history.stateon creation. push(to)callspushState;replace(to)callsreplaceState.- Listens to
popstateso back/forward buttons update subscribers. stateyou pass topush/replaceis stored inwindow.history.statealongside an internalkeyused for entry identity.
Memory history
import {createMemoryHistory} from 'tss-route-lib';
const history = createMemoryHistory({
initialEntries: ['/users/123'],
initialIndex: 0,
});- Holds an in-memory stack of entries.
pushappends and drops any forward entries (browser-like behavior).replaceswaps the current entry without changing the index.back()/forward()/go(delta)move within the stack and emitPOP.- Exposes additional read-only fields:
indexandentries.
This is the right choice for tests, server-rendered fragments, or any non-browser environment.
Working with To
Both implementations accept the same To shape:
type To = string | Partial<{pathname: string; search: string; hash: string}>;Strings are parsed (? and # are split off the pathname). Object form lets you skip parsing.
history.push('/users/1?tab=posts#top');
history.push({pathname: '/users/1', search: '?tab=posts', hash: '#top'});Subscribing manually
const unsub = history.listen((update) => {
console.log(update.action, update.location.pathname);
});
unsub();useLocation() already does this internally. You only need to call listen yourself for non-React code paths (analytics, logging, etc.).
Blocking transitions
Both built-in histories implement block(blocker). A registered blocker intercepts every push / replace / POP transition; the navigation only happens when the blocker calls tx.retry().
const unblock = history.block((tx) => {
if (confirm(`Leave for ${tx.location.pathname}?`)) {
tx.retry();
}
});
// Later, when the guard is no longer needed:
unblock();For React, prefer useBlocker — it wires the lifecycle for you and exposes a state machine instead of a synchronous confirm.
Only one blocker can be active per history instance; registering a new one replaces the previous.
Bring your own history
The router only depends on the History type from tss-route-lib. Implement that interface and you can plug in anything (a hash router, a stateful URL shim, etc.).
import type {History} from 'tss-route-lib';
const myHistory: History = {
/* ... */
};If you don't need transition blocking, block can be a no-op that returns a no-op unsubscribe. Without it, useBlocker simply never transitions to 'blocked'.