Skip to content

History

js
import { 
historyPush
,
historyReplace
,
objectToURLSearchParams
} from '@studiometa/js-toolkit/utils';

Writing the URL

js
import { 
historyPush
,
historyReplace
} from '@studiometa/js-toolkit/utils';
historyPush
({
path
: '/products',
search
: {
page
: 2,
sort
: 'price' } });
historyReplace
({
search
: {
page
: 3 } });
historyPush
({
hash
: 'section-2' });
ts
interface HistoryOptions {
  path?: string;
  search?: URLSearchParams | Record<string, SearchParamInput>;
  hash?: string;
}

Each part is optional, and an omitted part is kept. historyReplace({ search }) keeps the path and the hash; historyPush({ hash }) keeps the path and the query string.

That is the whole reason these exist over history.pushState(): rebuilding a URL from location by hand is where a filter update loses the hash, or a hash update drops the query string.

historyPush

ts
historyPush(options: HistoryOptions, data?: unknown, title?: string): void

Adds a history entry. This is what makes a state navigable with the back button.

historyReplace

ts
historyReplace(options: HistoryOptions, data?: unknown, title?: string): void

Replaces the current entry. This is what keeps a scroll position or a tab index out of the history.

Building the query string

objectToURLSearchParams

ts
objectToURLSearchParams(
  object: Record<string, SearchParamInput>,
  defaultSearch?: string,
): URLSearchParams
js
import { 
objectToURLSearchParams
} from '@studiometa/js-toolkit/utils';
objectToURLSearchParams
({
page
: 2,
tags
: ['a', 'b'],
open
: true });
objectToURLSearchParams
({
page
: 2 },
location
.
search
);

SearchParamInput accepts a string, a number, a boolean, null, undefined, an array of those, or a nested object of those — so a filter state serializes without being flattened first.

defaultSearch is the starting point, so the result merges into an existing query string instead of replacing it.

See also

For a URL that is also a store you can read, write and observe, use createUrlSearchParamsStorage() — it is these functions behind a typed key-value surface, with popstate wired up.

MIT Licensed