{"id":51135549,"url":"https://github.com/bmlt-enabled/svelte-spa-router","last_synced_at":"2026-06-25T17:30:53.619Z","repository":{"id":344679843,"uuid":"1128638992","full_name":"bmlt-enabled/svelte-spa-router","owner":"bmlt-enabled","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-19T12:14:47.000Z","size":2476,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-19T14:14:24.360Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/bmlt-enabled.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-06T00:04:57.000Z","updated_at":"2026-06-19T12:14:46.000Z","dependencies_parsed_at":null,"dependency_job_id":"c82a2ca6-3c49-4ca4-b6f6-b7b8acb5478d","html_url":"https://github.com/bmlt-enabled/svelte-spa-router","commit_stats":null,"previous_names":["bmlt-enabled/svelte-spa-router"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/bmlt-enabled/svelte-spa-router","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bmlt-enabled%2Fsvelte-spa-router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bmlt-enabled%2Fsvelte-spa-router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bmlt-enabled%2Fsvelte-spa-router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bmlt-enabled%2Fsvelte-spa-router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bmlt-enabled","download_url":"https://codeload.github.com/bmlt-enabled/svelte-spa-router/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bmlt-enabled%2Fsvelte-spa-router/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34786225,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":"2026-06-25T17:30:53.524Z","updated_at":"2026-06-25T17:30:53.603Z","avatar_url":"https://github.com/bmlt-enabled.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @bmlt-enabled/svelte-spa-router\n\nThis module is a router for [Svelte 5](https://github.com/sveltejs/svelte) applications, specifically optimized for Single Page Applications (SPA).\n\nMain features:\n\n- Supports both **hash-based routing** (default) and **History API path-based routing** for clean URLs\n- Insanely simple to use, and has a minimal footprint\n- Uses a vendored copy of the tiny [regexparam](https://github.com/lukeed/regexparam) for parsing routes, with support for parameters (e.g. `/book/:id?`) and more\n\nThis module is released under MIT license, forked from [ItalyPaleAle/svelte-spa-router](https://github.com/ItalyPaleAle/svelte-spa-router).\n\n## Routing modes\n\n### Hash-based routing (default)\n\nWith hash-based routing, navigation is possible thanks to storing the current view in the part of the URL after `#`, called \"hash\" or \"fragment\".\n\nFor example, if your SPA is in a static file called `index.html`, your URLs for navigating within the app look something like `index.html#/profile`, `index.html#/book/42`, etc. (The `index.html` part can usually be omitted for the index file, so you can just create URLs that look like `http://example.com/#/profile`).\n\nHash-based routing is simpler, works well even without a server, and it's generally better suited for static SPAs, especially when SEO isn't a concern, as is the case when the app requires authentication. Many popular apps use hash-based routing, including GMail!\n\n### Path-based routing (History API)\n\nIf you prefer clean URLs without the `#` fragment (e.g. `http://example.com/profile` instead of `http://example.com/#/profile`), you can enable path-based routing using the History API by setting `hashMode={false}` on the Router component:\n\n```svelte\n\u003cRouter {routes} hashMode={false} /\u003e\n```\n\nAll other APIs (`push`, `pop`, `replace`, `use:link`, `use:active`, route definitions) work exactly the same in both modes. No changes to your route definitions or navigation code are needed.\n\n#### Base path\n\nWhen your app is served under a sub-path (e.g. `example.com/meetings/`), you can set a `basePath` so the router automatically strips it from incoming URLs and prepends it to outgoing ones:\n\n```svelte\n\u003cRouter {routes} hashMode={false} basePath=\"/meetings\" /\u003e\n```\n\nOr programmatically:\n\n```js\nimport { setHashMode } from '@bmlt-enabled/svelte-spa-router'\nsetHashMode(false, '/meetings')\n```\n\nWith `basePath=\"/meetings\"`, `push('/book/42')` navigates to `/meetings/book/42`, and `router.location` returns `/book/42` — your app code never needs to know about the prefix.\n\n**Important:** When using path-based routing, your server must be configured to serve your `index.html` for all routes (SPA fallback). Without this, refreshing the page or navigating directly to a URL like `/books` will result in a 404 from the server. Common configurations:\n\n- **Nginx:** `try_files $uri $uri/ /index.html;`\n- **Apache:** Use `FallbackResource /index.html` or a rewrite rule\n- **Vite dev server:** Already handles this by default\n- **Static hosts (Netlify, Vercel):** Add a `_redirects` or `vercel.json` rewrite rule\n\n## Sample code\n\nCheck out the code in the [examples](/examples) folder for some usage examples.\n\nTo run the example, clone the repository, install the dependencies, then start the dev server:\n\n```sh\ngit clone https://github.com/bmlt-enabled/svelte-spa-router\ncd svelte-spa-router\nnpm install\n\n# Hash-mode example\nnpm run dev:example\n\n# Path-mode example (History API)\nnpm run dev:example-path\n\n# Test app (hash mode)\nnpm run dev:test\n\n# Test app (path mode / History API)\nnpm run dev:test-path\n```\n\n- Example app (hash mode): http://localhost:5050\n- Example app (path mode): http://localhost:5054\n- Test app (hash mode): http://localhost:5051\n- Test app (path mode): http://localhost:5052\n\n## Using @bmlt-enabled/svelte-spa-router\n\nYou can include the router in any project using Svelte 5.\n\n### Install from NPM\n\n```sh\nnpm install @bmlt-enabled/svelte-spa-router\n```\n\n### Supported browsers\n\n@bmlt-enabled/svelte-spa-router aims to support modern browsers, including recent versions of:\n\n- Chrome\n- Edge (\"traditional\" and Chromium-based)\n- Firefox\n- Safari\n\n### Define your routes\n\nEach route is a normal Svelte component, with the markup, scripts, bindings, etc. Any Svelte component can be a route.\n\nThe route definition is just a JavaScript dictionary (object) where the key is a string with the path (including parameters, etc), and the value is the route object.\n\nFor example:\n\n```js\nimport Home from './routes/Home.svelte'\nimport Author from './routes/Author.svelte'\nimport Book from './routes/Book.svelte'\nimport NotFound from './routes/NotFound.svelte'\n\nconst routes = {\n    // Exact path\n    '/': Home,\n\n    // Using named parameters, with last being optional\n    '/author/:first/:last?': Author,\n\n    // Wildcard parameter\n    '/book/*': Book,\n\n    // Catch-all\n    // This is optional, but if present it must be the last\n    '*': NotFound,\n}\n```\n\nRoutes must begin with `/` (or `*` for the catch-all route).\n\nAlternatively, you can also define your routes using custom regular expressions, as explained below.\n\nNote that the order matters! When your users navigate inside the app, the first matching path will determine which route to load. It's important that you leave any \"catch-all\" route (e.g. a \"Page not found\" one) at the end.\n\n### Include the router view\n\nTo display the router, in a Svelte component (usually `App.svelte`), first import the router component:\n\n```js\nimport Router from '@bmlt-enabled/svelte-spa-router'\n```\n\nThen, display the router anywhere you'd like by placing the component in the markup. For example:\n\n```svelte\n\u003cbody\u003e\n    \u003c!-- Hash-based routing (default) --\u003e\n    \u003cRouter {routes} /\u003e\n\n    \u003c!-- Or, for clean URLs using the History API --\u003e\n    \u003cRouter {routes} hashMode={false} /\u003e\n\u003c/body\u003e\n```\n\nThe `routes` prop is the dictionary defined above. The optional `hashMode` prop controls whether the router uses hash-based URLs (default, `true`) or the History API for clean path-based URLs (`false`). See [Routing modes](#routing-modes) for details.\n\nThat's it! You already have all that you need for a fully-functional routing experience.\n\n### Dynamically-imported components and code-splitting\n\n@bmlt-enabled/svelte-spa-router supports dynamically-imported components (via the `import()` construct). The advantage of using dynamic imports is that your bundler can enable code-splitting and reduce the size of the bundle sent to your users.\n\nTo use dynamically-imported components, you need to leverage the `wrap` method (which can be used for a variety of actions, as per the docs on [route wrapping](/AdvancedUsage.md#route-wrapping)). First, import the `wrap` method:\n\n```js\nimport { wrap } from '@bmlt-enabled/svelte-spa-router/wrap'\n```\n\nThen, in your route definition, wrap your routes using the `wrap` method, passing a function that returns the dynamically-imported component to the `asyncComponent` property:\n\n```js\nwrap({\n    asyncComponent: () =\u003e import('./Foo.svelte'),\n})\n```\n\n\u003e Note: the value of `asyncComponent` must be the **definition of a function** returning a dynamically-imported component, such as `asyncComponent: () =\u003e import('./Foo.svelte')`.  \n\u003e Do **not** use `asyncComponent: import('./Foo.svelte')`, which is a function invocation instead.\n\nFor example, to make the Author and Book routes from the first example dynamically-imported, we'd update the code to:\n\n```js\n// Import the wrap method\nimport { wrap } from '@bmlt-enabled/svelte-spa-router/wrap'\n\n// Note that Author and Book are not imported here anymore, so they can be imported at runtime\nimport Home from './routes/Home.svelte'\nimport NotFound from './routes/NotFound.svelte'\n\nconst routes = {\n    '/': Home,\n\n    // Wrapping the Author component\n    '/author/:first/:last?': wrap({\n        asyncComponent: () =\u003e import('./routes/Author.svelte'),\n    }),\n\n    // Wrapping the Book component\n    '/book/*': wrap({\n        asyncComponent: () =\u003e import('./routes/Book.svelte'),\n    }),\n\n    // Catch-all route last\n    '*': NotFound,\n}\n```\n\nThe `wrap` method accepts an object with multiple properties and enables other features, including: setting a \"loading\" component that is shown while a dynamically-imported component is being requested, adding pre-conditions (route guards), passing static props, and adding custom user data.\n\nYou can learn more about all the features of `wrap` in the documentation for [route wrapping](/AdvancedUsage.md#route-wrapping).\n\n### Navigating between pages\n\nIn hash mode (the default), you can navigate between pages with normal anchor (`\u003ca\u003e`) tags using the `#` prefix:\n\n```svelte\n\u003ca href=\"#/book/123\"\u003eThus Spoke Zarathustra\u003c/a\u003e\n```\n\nIn path mode (`hashMode={false}`), use regular paths:\n\n```svelte\n\u003ca href=\"/book/123\"\u003eThus Spoke Zarathustra\u003c/a\u003e\n```\n\n#### The `use:link` action\n\nThe `use:link` action works the same in both modes. You always write paths starting with `/` and the router handles the rest:\n\nRather than having to type `#` before each link, you can also use the `use:link` action:\n\n```svelte\n\u003cscript\u003e\n    import { link } from '@bmlt-enabled/svelte-spa-router'\n\u003c/script\u003e\n\n\u003ca href=\"/book/321\" use:link\u003eThe Little Prince\u003c/a\u003e\n```\n\nThe `use:link` action accepts an optional parameter `opts`, which can be one of:\n\n- A dictionary `{href: '/foo', disabled: false}` where both keys are optional:\n    - If you set a value for `href`, your link will be updated to point to that address, reactively (this will always take precedence over `href` attributes, if present)\n    - Setting `disabled: true` disables the link, so clicking on that would have no effect\n- A string with a destination (e.g. `/foo`), which is a shorthand to setting `{href: '/foo'}`.\n\nFor example:\n\n```svelte\n\u003cscript\u003e\n    import { link } from '@bmlt-enabled/svelte-spa-router'\n    let myLink = '/book/456'\n\u003c/script\u003e\n\n\u003c!-- Note the {{...}} notation because we're passing an object as parameter for a Svelte action --\u003e\n\u003ca use:link={{ href: myLink, disabled: false }}\u003eThe Biggest Princess\u003c/a\u003e\n```\n\nThe above is equivalent to:\n\n```svelte\n\u003cscript\u003e\n    import { link } from '@bmlt-enabled/svelte-spa-router'\n    let myLink = '/book/456'\n\u003c/script\u003e\n\n\u003ca use:link={myLink}\u003eThe Biggest Princess\u003c/a\u003e\n```\n\nChanging the value of `myLink` will reactively update the link's `href` attribute.\n\n#### Navigating programmatically\n\nYou can navigate between pages programmatically too:\n\n```js\nimport { push, pop, replace } from '@bmlt-enabled/svelte-spa-router'\n\n// The push(url) method navigates to another page, just like clicking on a link\npush('/book/42')\n\n// The pop() method is equivalent to hitting the back button in the browser\npop()\n\n// The replace(url) method navigates to a new page, but without adding a new entry in the browser's history stack\n// So, clicking on the back button in the browser would not lead to the page users were visiting before the call to replace()\nreplace('/book/3')\n```\n\nThese methods can be used inside Svelte markup too, for example:\n\n```svelte\n\u003cbutton onclick={() =\u003e push('/page')}\u003eGo somewhere\u003c/button\u003e\n```\n\nThe `push`, `pop` and `replace` methods perform navigation actions only in the next iteration (\"tick\") of the JavaScript event loop. This makes it safe to use them also inside `onMount` callbacks within Svelte components.\n\nThese functions return a Promise that resolves with no value once the navigation has been triggered (in the next tick of the event loop); however, please note that this will likely be before the new page has rendered.\n\n### Parameters from routes\n\n@bmlt-enabled/svelte-spa-router uses a vendored copy of [regexparam](https://github.com/lukeed/regexparam) (`regexparam.js`) to parse routes, so you can add optional parameters to the route. Basic syntax is:\n\n- `/path` matches `/path` exactly (and only that)\n- `/path/:id` matches `/path/` followed by any string, which is a named argument `id`\n- `/path/:id/:version?` allows for an optional second named argument `version`\n- `/path/*` matches `/path/` followed by anything, using a non-named argument\n\n_Please refer to [REGEXPARAM.md](REGEXPARAM.md) for more details._\n\nIf your route contains any parameter, they will be made available to your component inside the `params` dictionary.\n\nFor example, for a route `/name/:first/:last?`, you can create this Svelte component:\n\n```svelte\n\u003cscript\u003e\n    // You need to define the component prop \"params\"\n    let { params = {} } = $props()\n\u003c/script\u003e\n\n\u003cp\u003e\n    Your name is: \u003cb\u003e{params.first}\u003c/b\u003e\n    \u003cb\n        \u003e{#if params.last}{params.last}{/if}\u003c/b\n    \u003e\n\u003c/p\u003e\n```\n\nNon-named arguments are returned as `params.wild`.\n\n### Getting the current page\n\nYou can get the current page from `router.location`.\n\n```svelte\n\u003cscript\u003e\n    import { router } from '@bmlt-enabled/svelte-spa-router'\n\u003c/script\u003e\n\n\u003cp\u003eThe current page is: {router.location}\u003c/p\u003e\n```\n\nIf you need both location and querystring together, use `router.loc`.\n\n### Querystring parameters\n\nYou can extract querystring parameters from the URL. In hash mode, these are part of the hash fragment (e.g. `#/books?show=authors,titles\u0026order=1`). In path mode, they come from the real URL querystring (e.g. `/books?show=authors,titles\u0026order=1`).\n\nThe router separates the querystring from the location and returns it as a string in `router.querystring`. For example:\n\n```svelte\n\u003cscript\u003e\n    import { router } from '@bmlt-enabled/svelte-spa-router'\n\u003c/script\u003e\n\n\u003cp\u003eThe current page is: {router.location}\u003c/p\u003e\n\u003cp\u003eThe querystring is: {router.querystring}\u003c/p\u003e\n```\n\nWith the example above, this would print:\n\n```text\nThe current page is: /books\nThe querystring is: show=authors,titles\u0026order=1\n```\n\nTo keep this component lightweight, @bmlt-enabled/svelte-spa-router **does not parse** the \"querystring\". If you want to parse the value of `router.querystring`, you can use [URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) available in all modern browsers, or third-party modules such as [qs](https://www.npmjs.com/package/qs).\n\n### Highlight active links\n\n@bmlt-enabled/svelte-spa-router has built-in support for automatically marking links as \"active\", with the `use:active` action.\n\nFor example, you can use the code below to add the CSS class `active` to links that are active:\n\n```svelte\n\u003cscript\u003e\n    import { link } from '@bmlt-enabled/svelte-spa-router'\n    import active from '@bmlt-enabled/svelte-spa-router/active'\n\u003c/script\u003e\n\n\u003ca\n    href=\"/hello/user\"\n    use:link\n    use:active={{\n        path: '/hello/*',\n        className: 'active',\n        inactiveClassName: 'inactive',\n    }}\u003eSay hi!\u003c/a\n\u003e\n\u003ca href=\"/hello/user\" use:link use:active={'/hello/*'}\n    \u003eSay hi with a default className!\u003c/a\n\u003e\n\u003ca href=\"/hello/user\" use:link use:active\u003eSay hi with all default options!\u003c/a\u003e\n\n\u003cstyle\u003e\n    /* Style for \"active\" links; need to mark this :global because the router adds the class directly */\n    :global(a.active) {\n        color: red;\n    }\n\u003c/style\u003e\n```\n\nThe `active` action accepts a dictionary `options` as argument:\n\n- `options.path`: the path that, when matched, makes the link active. In the first example above, we want the link to be active when the route is `/hello/*` (the asterisk matches anything after that). As you can see, this doesn't have to be the same as the path the link points to. When `options.path` is omitted or false-y, it defaults to the path specified in the link's `href` attribute. This parameter can also be a regular expression that will mark the link as active when it matches: for example, setting to the regular expression `/^\\/*\\/hi$/` will make the link active when it starts with `/` and ends with `/hi`, regardless of what's in between.\n- `options.className`: the name of the CSS class to add. This is optional, and it defaults to `active` if not present.\n- `options.inactiveClassName`: the name of the CSS class to add when the link is _not_ active. This is optional, and it defaults to nothing if not present.\n\nAs a shorthand, instead of passing a dictionary as `options`, you can pass a single string or regular expression that will be interpreted as `options.path`.\n\n### Define routes with custom regular expressions\n\nIt's possible to define routes using custom regular expressions too, allowing for greater flexibility. However, this requires defining routes using a JavaScript Map rather than an object:\n\n```js\nimport Home from './routes/Home.svelte'\nimport Name from './routes/Name.svelte'\nimport NotFound from './routes/NotFound.svelte'\n\nconst routes = new Map()\n\n// You can still use strings to define routes\nroutes.set('/', Home)\nroutes.set('/hello/:first/:last?', Name)\n\n// The keys for the next routes are regular expressions\n// You will very likely always want to start the regular expression with ^\nroutes.set(/^\\/hola\\/(.*)/i, Name)\nroutes.set(/^\\/buongiorno(\\/([a-z]+))/i, Name)\n\n// Catch-all, must be last\nroutes.set('*', NotFound)\n```\n\nWhen you define routes as regular expressions, the `params` prop is populated with an array with the result of the matches from the regular expression.\n\nFor example, with this `Name.svelte` route:\n\n```svelte\n\u003cscript\u003e\n    // You need to define the component prop \"params\"\n    let { params = {} } = $props()\n\u003c/script\u003e\n\n\u003cp\u003eParams is: \u003ccode\u003e{JSON.stringify(params)}\u003c/code\u003e\u003c/p\u003e\n```\n\nWhen visiting `#/hola/amigos`, the params prop will be `[\"/hola/amigos\",\"amigos\"]`.\n\nThis is consistent with the results of [`RegExp.prototype.exec()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec).\n\n\u003e When defining a route using a regular expression, you can optionally use [named capturing groups](https://2ality.com/2017/05/regexp-named-capture-groups.html). When using those, in addition to finding your matches in the `params` prop, you can find the matches for named capturing groups in `params.group`.  \n\u003e For example, consider the route:\n\u003e\n\u003e ```js\n\u003e routes.set(/^\\/book\\/(?\u003ctitle\u003e[a-z]+)$/, Book)\n\u003e ```\n\u003e\n\u003e When visiting `/#/book/mytitle`, the `params` prop will be an array with `[\"/book/mytitle\", \"mytitle\"]`, and `params.groups` will be a dictionary with `{\"title\": \"mytitle\"}`.\n\n## Advanced usage\n\nCheck out the [Advanced Usage](/AdvancedUsage.md) documentation for using:\n\n- [Route wrapping](/AdvancedUsage.md#route-wrapping), including:\n    - [Dynamically-imported routes and placeholders](/AdvancedUsage.md#async-routes-and-loading-placeholders)\n    - [Route pre-conditions](/AdvancedUsage.md#route-pre-conditions) (\"route guards\")\n    - [Adding user data to routes](/AdvancedUsage.md#user-data)\n    - [Static props](/AdvancedUsage.md#static-props)\n- [`onRouteEvent`](/AdvancedUsage.md#onrouteevent)\n- [`onRouteLoading` and `onRouteLoaded`](/AdvancedUsage.md#onrouteloading-and-onrouteloaded)\n- [Querystring parsing](/AdvancedUsage.md#querystring-parsing)\n- [Static props](/AdvancedUsage.md#static-props)\n- [Route transitions](/AdvancedUsage.md#route-transitions)\n- [Nested routers](/AdvancedUsage.md#nested-routers)\n- [Route groups](/AdvancedUsage.md#route-groups)\n- [Restore scroll position](/AdvancedUsage.md#restore-scroll-position)\n- [Path-based routing (History API)](/AdvancedUsage.md#path-based-routing-history-api)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbmlt-enabled%2Fsvelte-spa-router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbmlt-enabled%2Fsvelte-spa-router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbmlt-enabled%2Fsvelte-spa-router/lists"}