{"id":27045036,"url":"https://github.com/mefechoel/svelte-navigator-history","last_synced_at":"2025-07-03T06:07:00.958Z","repository":{"id":57163876,"uuid":"302884028","full_name":"mefechoel/svelte-navigator-history","owner":"mefechoel","description":"History module for svelte-navigator (experimental)","archived":false,"fork":false,"pushed_at":"2022-06-04T10:09:33.000Z","size":742,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-09T02:11:32.048Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mefechoel.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-10-10T11:14:53.000Z","updated_at":"2022-06-04T10:04:57.000Z","dependencies_parsed_at":"2022-08-27T16:02:11.241Z","dependency_job_id":null,"html_url":"https://github.com/mefechoel/svelte-navigator-history","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mefechoel/svelte-navigator-history","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mefechoel%2Fsvelte-navigator-history","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mefechoel%2Fsvelte-navigator-history/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mefechoel%2Fsvelte-navigator-history/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mefechoel%2Fsvelte-navigator-history/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mefechoel","download_url":"https://codeload.github.com/mefechoel/svelte-navigator-history/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mefechoel%2Fsvelte-navigator-history/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263271501,"owners_count":23440396,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-04-05T05:31:25.425Z","updated_at":"2025-07-03T06:07:00.911Z","avatar_url":"https://github.com/mefechoel.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Svelte Navigator History\n\n[![npm package](https://img.shields.io/npm/v/@svelte-navigator/history.svg?style=flat-square)](https://npmjs.com/package/@svelte-navigator/history)\n[![npm bundle size](https://img.shields.io/bundlephobia/minzip/@svelte-navigator/history?style=flat-square)](https://bundlephobia.com/result?p=@svelte-navigator/history)\n[![NPM](https://img.shields.io/npm/l/@svelte-navigator/history?style=flat-square)](https://github.com/mefechoel/svelte-navigator-history/blob/main/LICENSE)\n[![GitHub last commit](https://img.shields.io/github/last-commit/mefechoel/svelte-navigator-history?style=flat-square)](https://github.com/mefechoel/svelte-navigator-history/commits/main)\n[![Code Style Prettier](https://img.shields.io/badge/code%20style-prettier-ff7fe1.svg?style=flat-square)](https://github.com/prettier/prettier#readme)\n[![Build Status](https://img.shields.io/github/workflow/status/mefechoel/svelte-navigator-history/Test?style=flat-square)](https://github.com/mefechoel/svelte-navigator-history/actions?query=workflow%3ATest)\n\nHistory module for\n[svelte-navigator](https://github.com/mefechoel/svelte-navigator). It abstracts\nthe management of the apps location using either the HTML5 History API, the hash\nfragment of the URL or an in-memory mode.\n\n⚠️⚠️⚠️ This is an **_experimental_** package, that will be used for the next\nversion of svelte-navigator. ⚠️⚠️⚠️\n\n## Table of Contents\n\n- [Build requirements](#build-requirements)\n- [API](#api)\n  - [`NavigatorHistory`](#navigatorhistory)\n  - [Browser History](#browser-history)\n    - [`createBrowserHistory`](#createbrowserhistory)\n  - [Hash History](#hash-history)\n    - [`createHashHistory`](#createhashhistory)\n  - [Memory History](#memory-history)\n    - [`createMemoryHistory`](#creatememoryhistory)\n  - [`parsePath`](#parsepath)\n  - [`stringifyPath`](#stringifypath)\n  - [`createNavigate`](#createnavigate)\n- [License](#license)\n\n## Build requirements\n\nSvelte Navigator History depends on a build process in which certain environment\nvariables are replaced. This is necessary in order to provide descriptive error\nmessage in development, while keeping the production bundles lean.\n\nIf you're using rollup you can use\n[`@rollup/plugin-replace`](https://github.com/rollup/plugins/tree/master/packages/replace)\nlike so:\n\n```js\n// rollup.config.js\nimport replace from \"@rollup/plugin-replace\";\n\nconst isDev = Boolean(process.env.ROLLUP_WATCH);\nconst nodeEnv = isDev ? \"development\" : \"production\";\n\nexport default {\n\t// ...\n\tplugins: [\n\t\t// ...\n\t\treplace({ \"process.env.NODE_ENV\": JSON.stringify(nodeEnv) }),\n\t],\n};\n```\n\nIf you're using webpack you can use\n[`webpack.DefinePlugin`](https://webpack.js.org/plugins/define-plugin/):\n\n```js\n// webpack.config.js\nconst webpack = require(\"webpack\");\n\nmodule.exports = {\n\t// ...\n\tplugins: [\n\t\t// ...\n\t\tnew webpack.DefinePlugin({\n\t\t\t\"process.env.NODE_ENV\": JSON.stringify(process.env.NODE_ENV),\n\t\t}),\n\t],\n};\n```\n\nIf you don't have access to your build pipeline, `@svelte-navigator/history`\nprovides pre-built development and production bundles you can use. Each are\navailable as es-module and umd bundle. Import the desired format from the `dist`\nfolder:\n\n```js\nimport * as History from \"@svelte-navigator/history/dist/history.development.mjs\";\nimport * as History from \"@svelte-navigator/history/dist/history.production.mjs\";\nimport * as History from \"@svelte-navigator/history/dist/history.development.umd.js\";\nimport * as History from \"@svelte-navigator/history/dist/history.production.umd.js\";\n```\n\n## API\n\n### `NavigatorHistory`\n\nEach History implements the following interface:\n\n```ts\ninterface NavigatorHistory\u003cState = unknown\u003e {\n\tpush: (uri: string, state?: State | null) =\u003e void;\n\treplace: (uri: string, state?: State | null) =\u003e void;\n\tgo: (delta: number) =\u003e void;\n\tnavigate: NavigateFn\u003cState\u003e;\n\n\treadonly location: NavigatorLocation\u003cState\u003e;\n\treadonly action: Action;\n\n\tsubscribe: (subscriber: Subscriber\u003cHistoryUpdate\u003cState\u003e\u003e) =\u003e () =\u003e void;\n\tcreateHref: (to: string) =\u003e string;\n\trelease: () =\u003e void;\n}\n```\n\n#### `push`\n\nCalling `history.push` navigates to a new URL and optionally pushes a value to\nthe history stack. The state is an arbitrary (serializable) value. It can be\nthought of as something like the message body of an http post request. It\ncontains details which are not visible in the URL, but are bound to the specific\nrequest.\n\n```js\n// Navigate to \"/blog?id=123\"\nhistory.push(\"/blog?id=123\");\n\n// Navigate to \"/about\" and push an object to the stack, which can\n// be read by accessing `history.location.state`\nhistory.push(\"/about\", { from: \"/blog\" });\n```\n\n#### `replace`\n\nCalling `history.replace` replaces the current URL and optionally the value of\nthe history stack.\n\n```js\n// Replace current entry, which could be a protected route, with \"/login\"\nhistory.replace(\"/login\");\n\n// Save the location we're coming from, so we can navigate back, when\n// login was successfull\nhistory.replace(\"/login\", { from: history.location });\n```\n\n#### `go`\n\n`history.go` allows you to navigate the history stack, similar to using the back\nand forward buttons of the browser.\n\n```js\nconst goBack = () =\u003e history.go(-1);\nconst goForward = () =\u003e history.go(1);\n```\n\n#### `navigate`\n\n`history.navigate` is a convenience method, that combines the functionality of\n`push`, `replace` and `go`.\n\n```js\n// Go to \"/blog?id=123\"\nhistory.navigate(\"/blog?id=123\");\n\n// Replace current entry and go to \"/login\".\n// Save the location we're coming from, so we can navigate back,\n// when login was successfull\nhistory.navigate(\"/login\", {\n\tstate: { from: history.location },\n\treplce: true,\n});\n\n// Go back one entry in the history stack\nhistory.navigate(-1);\n```\n\n#### `location`\n\nThe location represents the current state of the URL. It is very similar to the\nbuilt-in `window.location`, in that it has properties for the `pathname`,\n`search` and `hash` of the URL. It can however also carry a state, that can be\nset when changing location.\n\nA location looks like this:\n\n```ts\ninterface NavigatorLocation\u003cState = unknown\u003e {\n\tpathname: string;\n\t// `search` and `hash` are `\"\"`, when they are not present in the URL.\n\t// When they are, they begin with a `\"?\"` or a `\"#\"` respectively\n\tsearch: string;\n\thash: string;\n\tstate: State | null;\n\t// `key` is a unique id for each location entry. It can be used to\n\t// reference a location entry elsewhere, e.g. when storing entries\n\t// in `localStorage`\n\tkey: string;\n}\n```\n\n#### `action`\n\nThe action represents what event lead to the change in location. Possible values\nare:\n\n- `\"POP\"`: The default value. It is caused by hitting the back or forward button\n  of the browser, or by calling `history.go`.\n- `\"PUSH\"`: A new entry has been pushed to the history stack, by calling\n  `history.push`. This is what you want to use when navigating programmatically\n  in your app.\n- `\"REPLACE\"`: An entry of the history stack has been replaced with another one.\n  This is caused when `history.replace` has been called.\n\n#### `subscribe`\n\nYou can register an event handler by calling `history.subscribe`. The handler\nyou pass to `subscribe` is called with the location and action of the latest\nnavigation. It is also called when the subscriber is registered. `subscribe`\nreturns an `unsubscribe` function, which when called removes the event\nsubscriber.\n\n```js\nconst unsubscribe = history.subscribe(({ location, action }) =\u003e {\n\tconst url = `${location.pathname}${location.search}${location.hash}`;\n\tconsole.log(`Action: ${action}; Url: ${url}`);\n});\n\n// ... do something ...\n\n// Remove the subscriber\nunsubscribe();\n```\n\n#### `createHref`\n\n`history.createHref` takes a path and returns a string, you can use as a `href`\nattribute of an `\u003ca\u003e` element. For hash routing for example, a `\"#\"` has to be\nprepended to the beginning of a path.\n\n#### `release`\n\nA history might need to attach some global event handlers to be able to react to\nchanges in location. If you're for whatever reason creating lots of history\ninstances (which is probably a bad idea...), you should call `history.release`\nwhen you don't need the instance anymore. It will unregister all event handlers\nand allow the instance to be garbage collected, preventing memory leaks.\n\n### Browser History\n\nBrowser history uses the\n[HTML5 History API](https://developer.mozilla.org/en-US/docs/Web/API/History) to\nstore app's location and state in the URL. This is probably the best choice for\nmost apps, as it enables best SEO possibilities and will be most intuitive for\nmost users. This setup however needs some additional work because you need to\nconfigure your server to always serve your index.html file when a request\ndoesn't match a file. You can read more about it in\n[vue-routers docs about history routing](https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations).\n\n#### `createBrowserHistory`\n\nCreate an instance of a browser history. It will update the URL when the app's\nlocation changes. It will also listen to navigation events dispatched by the\nbrowser after the back and forward buttons have been clicked.\n\nWhen using `createBrowserHistory` **don't interact with the browsers `history`\nobject yourself**. It will lead to inconsistent states and you won't have a good\ntime debugging that... Also, always use the created history instance to read the\ncurrent location, and **don't use the browsers `location` object**. Again\ninconsistent location states aren't fun.\n\n```js\nimport { createBrowserHistory } from \"@svelte-navigator/history\";\n\nconst history = createBrowserHistory();\n\nhistory.subscribe(console.log);\n\nhistory.navigate(\"/blog?id=123\");\n```\n\n### Hash History\n\nHash history uses the hash fragment of the URL to simulate routing via URL. This\napproach is great if you want to get started quickly or if you don't have access\nto your server's configuration.\n\n#### `createHashHistory`\n\nCreate an instance of a hash history. It will change the hash of the URL when\nthe app's location changes.\n\nAgain, you should not interact with the global `history` or `location` objects\nyourself.\n\n```js\nimport { createHashHistory } from \"@svelte-navigator/history\";\n\nconst history = createHashHistory();\n\nhistory.subscribe(console.log);\n\nhistory.navigate(\"/blog?id=123\");\n```\n\n### Memory History\n\nMemory history keeps the location state of your app in memory. This is mainly\nuseful for testing if you don't run your tests in a browser. You could also use\na memory history if you want to control the state of a widget you embed in\nanother app using a router.\n\n#### `createMemoryHistory`\n\nCreate an instance of a hash history. It will change the hash of the URL when\nthe app's location changes.\n\nAgain, you should not interact with the global `history` or `location` objects\nyourself.\n\n```js\nimport { createMemoryHistory } from \"@svelte-navigator/history\";\n\nconst history = createMemoryHistory();\n\nhistory.subscribe(console.log);\n\nhistory.navigate(\"/blog?id=123\");\n```\n\n### `parsePath`\n\nCreate a location object from a URL string.\n\n```js\nimport { parsePath } from \"@svelte-navigator/history\";\n\nconst path = \"/search?q=falafel#result-3\";\nconst location = parsePath(path);\n// -\u003e {\n//   pathname: \"/search\",\n//   search: \"?q=falafel\",\n//   hash: \"#result-3\",\n// };\n```\n\n### `stringifyPath`\n\nJoins a location object to one path string.\n\n```js\nimport { stringifyPath } from \"@svelte-navigator/history\";\n\nconst location = {\n\tpathname: \"/search\",\n\tsearch: \"?q=falafel\",\n\thash: \"#result-3\",\n};\nconst path = stringifyPath(location);\n// -\u003e \"/search?q=falafel#result-3\"\n```\n\n### `createNavigate`\n\nCreate the `createNavigate` convenience method expected in the\n`NavigatorHistory` interface. Pass a Partial `NavigatorHistory` object, which\nimplements the `push`, `replace` and `go` methods, to the factory function.\n\n```js\nimport { createNavigate } from \"@svelte-navigator/history\";\n\nconst customHistory = {\n\tpush(uri, state) {\n\t\tconsole.log(\"PUSH\");\n\t},\n\treplace(uri, state) {\n\t\tconsole.log(\"REPLACE\");\n\t},\n\tgo(delta) {\n\t\tconsole.log(\"GO\");\n\t},\n};\n\ncustomHistory.navigate = createNavigate(customHistory);\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmefechoel%2Fsvelte-navigator-history","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmefechoel%2Fsvelte-navigator-history","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmefechoel%2Fsvelte-navigator-history/lists"}