{"id":13660743,"url":"https://github.com/andreasbm/router-slot","last_synced_at":"2025-04-10T01:12:44.842Z","repository":{"id":31234843,"uuid":"126738219","full_name":"andreasbm/router-slot","owner":"andreasbm","description":"A powerful web component router.","archived":false,"fork":false,"pushed_at":"2023-03-02T09:36:29.000Z","size":1470,"stargazers_count":118,"open_issues_count":30,"forks_count":20,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-10T01:12:41.925Z","etag":null,"topics":["customelements","pwa","router","webapp","webcomponents"],"latest_commit_sha":null,"homepage":"https://codepen.io/andreasbm/pen/XWWZpvM","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/andreasbm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-03-25T20:40:07.000Z","updated_at":"2025-02-09T01:15:07.000Z","dependencies_parsed_at":"2024-01-15T20:50:50.281Z","dependency_job_id":"ab4712fb-cdca-42a3-a524-3a8f868a9974","html_url":"https://github.com/andreasbm/router-slot","commit_stats":{"total_commits":269,"total_committers":4,"mean_commits":67.25,"dds":"0.13382899628252787","last_synced_commit":"df50b1696bcb8bf5e4a1fdf02e4ba5c77e9ef39d"},"previous_names":["andreasbm/web-router"],"tags_count":73,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreasbm%2Frouter-slot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreasbm%2Frouter-slot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreasbm%2Frouter-slot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreasbm%2Frouter-slot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andreasbm","download_url":"https://codeload.github.com/andreasbm/router-slot/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248137890,"owners_count":21053775,"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":["customelements","pwa","router","webapp","webcomponents"],"created_at":"2024-08-02T05:01:25.262Z","updated_at":"2025-04-10T01:12:44.823Z","avatar_url":"https://github.com/andreasbm.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003c!-- ⚠️ This README has been generated from the file(s) \"blueprint.md\" ⚠️--\u003e\u003ch1 align=\"center\"\u003erouter-slot\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n\t\t\u003ca href=\"https://npmcharts.com/compare/router-slot?minimal=true\"\u003e\u003cimg alt=\"Downloads per month\" src=\"https://img.shields.io/npm/dm/router-slot.svg\" height=\"20\"/\u003e\u003c/a\u003e\r\n\u003ca href=\"https://www.npmjs.com/package/router-slot\"\u003e\u003cimg alt=\"NPM Version\" src=\"https://img.shields.io/npm/v/router-slot.svg\" height=\"20\"/\u003e\u003c/a\u003e\r\n\u003ca href=\"https://david-dm.org/andreasbm/router-slot\"\u003e\u003cimg alt=\"Dependencies\" src=\"https://img.shields.io/david/andreasbm/router-slot.svg\" height=\"20\"/\u003e\u003c/a\u003e\r\n\u003ca href=\"https://github.com/andreasbm/router-slot/graphs/contributors\"\u003e\u003cimg alt=\"Contributors\" src=\"https://img.shields.io/github/contributors/andreasbm/router-slot.svg\" height=\"20\"/\u003e\u003c/a\u003e\r\n\u003ca href=\"https://www.webcomponents.org/element/router-slot\"\u003e\u003cimg alt=\"Published on webcomponents.org\" src=\"https://img.shields.io/badge/webcomponents.org-published-blue.svg\" height=\"20\"/\u003e\u003c/a\u003e\n\t\u003c/p\u003e\n\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003eA powerful web component router\u003c/b\u003e\u003c/br\u003e\n  \u003csub\u003eA router interprets the browser URL and navigates to a specific views based on the configuration. This router is optimized for routing between web components. If you want to play with it yourself, go to \u003ca href='https://codepen.io/andreasbm/pen/XWWZpvM'\u003ethe playground\u003c/a\u003e. Go here to see a demo \u003ca href=\"https://appnest-demo.firebaseapp.com/router-slot\"\u003ehttps://appnest-demo.firebaseapp.com/router-slot\u003c/a\u003e.\u003csub\u003e\n\u003c/p\u003e\n\n\u003cbr /\u003e\n\n\n* 😴  Lazy loading of routes\r\n* 🎁  Web component friendly\r\n* 📡  Easy to use API\r\n* 🛣  Specify params in the path\r\n* 👌  Zero dependencies\r\n* 📚  Uses the [history API](https://developer.mozilla.org/en-US/docs/Web/API/History_API)\r\n* 🎉  Supports routes for dialogs\r\n* 🛡  Supports guards for routes\r\n* ⚓️  Allows the anchor element for navigating\r\n* ⚙️  Very customizable\r\n* 🤐  2kb gzipped\n\n\u003cdetails\u003e\n\u003csummary\u003e📖 Table of Contents\u003c/summary\u003e\n\u003cbr /\u003e\n\r\n[![-----------------------------------------------------](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/colored.png)](#table-of-contents)\r\n\r\n## ➤ Table of Contents\n\n* [➤ Installation](#-installation)\r\n* [➤ Usage](#-usage)\r\n\t* [1. Add `\u003cbase href=\"/\"\u003e`](#1-add-base-href)\r\n\t* [2. Import the router](#2-import-the-router)\r\n\t* [3. Add the `\u003crouter-slot\u003e` element](#3-add-the-router-slot-element)\r\n\t* [4. Configure the router](#4-configure-the-router)\r\n\t* [5. Navigate using the history API, anchor tag or the `\u003crouter-link\u003e` component](#5-navigate-using-the-history-api-anchor-tag-or-the-router-link-component)\r\n\t\t* [History API](#history-api)\r\n\t\t* [Anchor element](#anchor-element)\r\n\t\t* [`router-link`](#router-link)\r\n\t* [6. Putting it all together](#6-putting-it-all-together)\r\n* [➤ `lit`](#-lit)\r\n* [➤ Advanced](#-advanced)\r\n\t* [Guards](#guards)\r\n\t* [Dialog routes](#dialog-routes)\r\n\t* [Params](#params)\r\n\t* [Deep dive into the different route kinds](#deep-dive-into-the-different-route-kinds)\r\n\t\t* [Component routes](#component-routes)\r\n\t\t* [Redirection routes](#redirection-routes)\r\n\t\t* [Resolver routes](#resolver-routes)\r\n\t* [Stop the user from navigating away](#stop-the-user-from-navigating-away)\r\n\t* [Helper functions](#helper-functions)\r\n\t* [Global navigation events](#global-navigation-events)\r\n\t\t* [Scroll to the top](#scroll-to-the-top)\r\n\t\t* [Style the active link](#style-the-active-link)\r\n* [➤ ⚠️ Be careful when navigating to the root!](#--be-careful-when-navigating-to-the-root)\r\n* [➤ Contributors](#-contributors)\r\n* [➤ License](#-license)\n\u003c/details\u003e\n\n\r\n[![-----------------------------------------------------](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/colored.png)](#installation)\r\n\r\n## ➤ Installation\n\n```node\nnpm i router-slot\n```\n\n\r\n[![-----------------------------------------------------](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/colored.png)](#usage)\r\n\r\n## ➤ Usage\n\nThis section will introduce how to use the router. If you hate reading and love coding you can go to the [playgroud](https://codepen.io/andreasbm/pen/XWWZpvM) to try it for yourself.\n\n### 1. Add `\u003cbase href=\"/\"\u003e`\n\nTo turn your app into a single-page-application you first need to add a [`\u003cbase\u003e` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) to the `index.html` in the `\u003chead\u003e`. If your file is located in the root of your server, the `href` value should be the following:\n\n```html\n\u003cbase href=\"/\"\u003e\n```\n\n### 2. Import the router\n\nTo import the library you'll need to import the dependency in your application.\n\n```javascript\nimport \"router-slot\";\n```\n\n### 3. Add the `\u003crouter-slot\u003e` element\n\nThe `router-slot` component acts as a placeholder that marks the spot in the template where the router should display the components for that route part.\n\n```html\n\u003crouter-slot\u003e\n  \u003c!-- Routed components will go here --\u003e\n\u003c/router-slot\u003e\n```\n\n### 4. Configure the router\n\nRoutes are added to the router through the `add` function on a `router-slot` component. Specify the parts of the path you want it to match with or use the `**` wildcard to catch all paths. The router has no routes until you configure it. The example below creates three routes. The first route path matches urls starting with `login` and will lazy load the login component. Remember to export the login component as default in the `./pages/login` file like this `export default LoginComponent extends HTMLElement { ... }`. The second route matches all urls starting with `home` and will stamp the `HomeComponent` in the `router-slot`. The third route matches all paths that the two routes before didn't catch and redirects to home. This can also be useful for displaying \"404 - Not Found\" pages.\n\n```javascript\nconst routerSlot = document.querySelector(\"router-slot\");\nawait routerSlot.add([\n  {\n    path: \"login\",\n    component: () =\u003e import(\"./path/to/login/component\") // Lazy loaded\n  },\n  {\n    path: \"home\",\n    component: HomeComponent // Not lazy loaded\n  },\n  {\n    path: \"**\",\n    redirectTo: \"home\"\n  }\n]);\n```\n\nYou may want to wrap the above in a `whenDefined` callback to ensure the `router-slot` exists before using its logic.\n\n```javascript\ncustomElements.whenDefined(\"router-slot\").then(async () =\u003e {\n  ...\n});\n```\n\n### 5. Navigate using the history API, anchor tag or the `\u003crouter-link\u003e` component\n\nIn order to change a route you can either use the [`history API`](https://developer.mozilla.org/en-US/docs/Web/API/History), use an anchor element or use the `router-link` component.\n\n#### History API\n\nTo push a new state into the history and change the URL you can use the `.pushState(...)` function on the history object.\n\n```javascript\nhistory.pushState(null, \"\", \"/login\");\n```\n\nIf you want to replace the current URL with another one you can use the `.replaceState(...)` function on the history object instead.\n\n```javascript\nhistory.replaceState(null, \"\", \"/login\");\n```\n\nYou can also go back and forth between the states by using the `.back()` and `.forward()` functions.\n\n```javascript\nhistory.back();\nhistory.forward();\n```\n\nGo [`here`](https://developer.mozilla.org/en-US/docs/Web/API/History) to read more about the history API.\n\n#### Anchor element\n\nNormally an [`anchor element`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) reloads the page when clicked. This library however changes the default behavior of all anchor element to use the history API instead.\n\n```html\n\u003ca href=\"/home\"\u003eGo to home!\u003c/a\u003e\n```\n\nThere are many advantages of using an anchor element, the main one being accessibility.\n\nAlternatively, if you would still like to allow relative links to other parts of your site to navigate as normally, you can opt out of this behavior on a link-by-link basis:\n\n```html\n\u003ca href=\"/about\" data-router-slot=\"disabled\"\u003eGo to about!\u003c/a\u003e\n```\n\n#### `router-link`\n\nWith the `router-link` component you add `\u003crouter-link\u003e` to your markup and specify a path. Whenever the component is clicked it will navigate to the specified path. Whenever the path of the router link is active the active attribute is set.\n\n```html\n\u003crouter-link path=\"login\"\u003e\n  \u003cbutton\u003eGo to login page!\u003c/button\u003e\n\u003c/router-link\u003e\n```\n\nPaths can be specified either in relative or absolute terms. To specify an absolute path you simply pass `/home/secret`. The slash makes the URL absolute. To specify a relative path you first have to be aware of the `router-slot` context you are navigating within. The `router-link` component will navigate based on the nearest parent `router-slot` element. If you give the component a path (without the slash), the navigation will be done in relation to the parent `router-slot`. You can also specify `../login` to traverse up the router tree.\n\n### 6. Putting it all together\n\nSo to recap the above steps, here's how to use the router.\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cbase href=\"/\" /\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003crouter-slot\u003e\u003c/router-slot\u003e\n\n    \u003ca href=\"/home\"\u003eGo to home\u003c/a\u003e\n    \u003ca href=\"/login\"\u003eGo to login\u003c/a\u003e\n\n    \u003cscript type=\"module\"\u003e\n        import \"https://unpkg.com/router-slot?module\";\n        customElements.whenDefined(\"router-slot\").then(async () =\u003e {\n            const routerSlot = document.querySelector(\"router-slot\");\n            await routerSlot.add([\n              {\n                path: \"login\",\n                component: () =\u003e import(\"./path/to/login-component\") \n              },\n              {\n                path: \"home\",\n                component: () =\u003e import(\"./path/to/home-component\") \n              },\n              {\n                path: \"**\",\n                redirectTo: \"home\"\n              }\n            ]);\n        });\n    \u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n\r\n[![-----------------------------------------------------](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/colored.png)](#lit)\r\n\r\n## ➤ `lit`\n\nThe `router-slot` works very well with `lit`. Check out the example below to get an idea on how you could use this router in your own `lit` based projects.\n\n```typescript\nimport { LitElement, html, PropertyValues } from \"lit\";\nimport { query, customElement } from \"lit/decorators.js\";\nimport { RouterSlot } from \"router-slot\";\n\nconst ROUTES = [\n {\n   path: \"login\",\n   component: () =\u003e import(\"./pages/login\")\n },\n {\n   path: \"home\",\n   component: () =\u003e import(\"./pages/home\")\n },\n {\n   path: \"**\",\n   redirectTo: \"home\"\n }\n];\n\n@customElement(\"app-component\");\nexport class AppComponent extends LitElement {\n  @query(\"router-slot\") $routerSlot!: RouterSlot;\n\n  firstUpdated (props: PropertyValues) {\n    super.firstUpdated(props);\n    this.$routerSlot.add(ROUTES);\n  }\n\n  render () {\n    return html`\u003crouter-slot\u003e\u003c/router-slot\u003e`;\n  }\n}\n```\n\n\r\n[![-----------------------------------------------------](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/colored.png)](#advanced)\r\n\r\n## ➤ Advanced\n\nYou can customize a lot in this library. Continue reading to learn how to handle your new superpowers.\n\n### Guards\n\nA guard is a function that determines whether the route can be activated or not. The example below checks whether the user has a session saved in the local storage and redirects the user to the login page if the access is not provided. If a guard returns false the routing is cancelled.\n\n```typescript\nfunction sessionGuard () {\n\n  if (localStorage.getItem(\"session\") == null) {\n    history.replaceState(null, \"\", \"/login\");\n    return false;\n  }\n\n  return true;\n}\n```\n\nAdd this guard to the add function in the `guards` array.\n\n```typescript\n...\nawait routerSlot.add([\n  ...\n  {\n    path: \"home\",\n    component: HomeComponent,\n    guards: [sessionGuard]\n  },\n  ...\n]);\n```\n\n### Dialog routes\n\nSometimes you wish to change the url without triggering the route change. This could for example be when you want an url for your dialog. To change the route without triggering the route change you can use the functions on the native object on the history object. Below is an example on how to show a dialog without triggering the route change.\n\n```javascript\nhistory.native.pushState(null, \"\", \"dialog\");\nalert(\"This is a dialog\");\nhistory.native.back();\n```\n\nThis allow dialogs to have a route which is especially awesome on mobile.\n\n### Params\n\nIf you want params in your URL you can do it by using the `:name` syntax. Below is an example on how to specify a path that matches params as well. This route would match urls such as `user/123`, `user/@andreas`, `user/abc` and so on. The preferred way of setting the value of the params is by setting it through the setup function.\n\n```typescript\nawait routerSlot.add([\n  {\n    path: \"user/:userId\",\n    component: UserComponent,\n    setup: (component: UserComponent, info: RoutingInfo) =\u003e {\n      component.userId = info.match.params.userId;\n    }\n  }\n]);\n```\n\nAlternatively you can get the params in the `UserComponent` by using the `queryParentRouterSlot(...)` function.\n\n```typescript\nimport { LitElement, html } from \"lit\";\nimport { Params, queryParentRouterSlot } from \"router-slot\";\n\nexport default class UserComponent extends LitElement {\n\n  get params (): Params {\n    return queryParentRouterSlot(this)!.match!.params;\n  }\n\n  render () {\n    const {userId} = this.params;\n    return html`\n      \u003cp\u003e:userId = \u003cb\u003e${userId}\u003c/b\u003e\u003c/p\u003e\n    `;\n  }\n}\n\ncustomElements.define(\"user-component\", UserComponent);\n```\n\n### Deep dive into the different route kinds\n\nThere exists three different kinds of routes. We are going to take a look at those different kinds in a bit, but first you should be familiar with what all routes have in common.\n\n```typescript\nexport interface IRouteBase\u003cT = any\u003e {\n\n  // The path for the route fragment\n  path: PathFragment;\n\n  // Optional metadata\n  data?: T;\n\n  // If guard returns false, the navigation is not allowed\n  guards?: Guard[];\n\n  // The type of match.\n  // - If \"prefix\" router-slot will try to match the first part of the path.\n  // - If \"suffix\" router-slot will try to match the last part of the path.\n  // - If \"full\" router-slot will try to match the entire path.\n  // - If \"fuzzy\" router-slot will try to match an arbitrary part of the path.\n  pathMatch?: PathMatch;\n}\n```\n\n#### Component routes\n\nComponent routes resolves a specified component. You can provide the `component` property with either a [class that extends HTMLElement](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements), a module that exports the `web component` as default or a DOM element. These three different ways of doing it can be done lazily by returning it a function instead.\n\n```typescript\nexport interface IComponentRoute extends IRouteBase {\n\n  // The component loader (should return a module with a default export if it is a module)\n  component: Class | ModuleResolver | PageComponent | (() =\u003e Class) | (() =\u003e PageComponent) | (() =\u003e ModuleResolver);\n\n  // A custom setup function for the instance of the component.\n  setup?: Setup;\n}\n```\n\nHere's an example on how that could look in practice.\n\n```typescript\nrouterSlot.add([\n  {\n    path: \"home\",\n    component: HomeComponent\n  },\n  {\n    path: \"terms\",\n    component: () =\u003e import(\"/path/to/terms-module\")\n  },\n  {\n    path: \"login\",\n    component: () =\u003e {\n      const $div = document.createElement(\"div\");\n      $div.innerHTML = `🔑 This is the login page`;\n      return $div;\n    }\n  },\n  {\n    path: \"video\",\n    component: document.createElement(\"video\")\n  }\n]);\n```\n\n#### Redirection routes\n\nA redirection route is good to use to catch all of the paths that the routes before did not catch. This could for example be used to handle \"404 - Page not found\" cases.\n\n```typescript\nexport interface IRedirectRoute extends IRouteBase {\n\n  // The paths the route should redirect to. Can either be relative or absolute.\n  redirectTo: string;\n\n  // Whether the query should be preserved when redirecting.\n  preserveQuery?: boolean;\n}\n```\n\nHere's an example on how that could look in practice.\n\n```typescript\nrouterSlot.add([\n  ...\n  {\n    path: \"404\",\n    component: document.createTextNode(`404 - The page you are looking for wasn't found.`)\n  }\n  {\n    path: \"**\",\n    redirectTo: \"404\",\n    preserveQuery: true\n  }\n]);\n```\n\n#### Resolver routes\n\nUse the resolver routes when you want to customize what should happen when the path matches the route. This is good to use if you for example want to show a dialog instead of navigating to a new component. If the custom resolver returns false the navigation will be cancelled.\n\n```typescript\nexport interface IResolverRoute extends IRouteBase {\n\n  // A custom resolver that handles the route change\n  resolve: CustomResolver;\n}\n```\n\nHere's an example on how that could look in practice.\n\n```typescript\nrouterSlot.add([\n  {\n    path: \"home\",\n    resolve: (info: RoutingInfo) =\u003e {\n      const $page = document.createElement(\"div\");\n      $page.appendChild(document.createTextNode(\"This is a custom home page!\"));\n\n      // You can for example add the page to the body instead of the\n      // default behavior where it is added to the router-slot.\n      // If you want a router-slot inside the element you are adding here\n      // you need to set the parent of that router-slot to info.slot.\n      document.body.appendChild($page);\n    })\n  }\n]);\n```\n\n### Stop the user from navigating away\n\nLet's say you have a page where the user has to enter some important data and suddenly he/she clicks on the back button! Luckily you can cancel the the navigation before it happens by listening for the `willchangestate` event on the `window` object and calling `preventDefault()` on the event.\n\n```javascript\nwindow.addEventListener(\"willchangestate\", e =\u003e {\n\n  // Check if we should navigate away from this page\n  if (!confirm(\"You have unsafed data. Do you wish to discard it?\")) {\n    e.preventDefault();\n    return;\n  }\n\n}, {once: true});\n```\n\n### Helper functions\n\nThe library comes with a set of helper functions. This includes:\n\n* `path()` - The current path of the location.\n* `query()` - The current query as an object.\n* `queryString()` - The current query as a string.\n* `toQuery(queryString)` - Turns a query string into a an object.\n* `toQueryString(query)` - Turns a query object into a string.\n* `slashify({ startSlash?: boolean, endSlash?: boolean; })` - Makes sure that the start and end slashes are present or not depending on the options.\n* `stripSlash()` - Strips the slash from the start and/or end of a path.\n* `ensureSlash()` - Ensures the path starts and/or ends with a slash.\n* `isPathActive (path: string | PathFragment, fullPath: string = getPath())` - Determines whether the path is active compared to the full path.\n\n### Global navigation events\n\nYou are able to listen to the navigation related events that are dispatched every time something important happens. They are dispatched on the `window` object.\n\n```typescript\n// An event triggered when a new state is added to the history.\nwindow.addEventListener(\"pushstate\", (e: PushStateEvent) =\u003e {\n  console.log(\"On push state\", path());\n});\n\n// An event triggered when the current state is replaced in the history.\nwindow.addEventListener(\"replacestate\", (e: ReplaceStateEvent) =\u003e {\n  console.log(\"On replace state\", path());\n});\n\n// An event triggered when a state in the history is popped from the history.\nwindow.addEventListener(\"popstate\", (e: PopStateEvent) =\u003e {\n  console.log(\"On pop state\", path());\n});\n\n// An event triggered when the state changes (eg. pop, push and replace)\nwindow.addEventListener(\"changestate\", (e: ChangeStateEvent) =\u003e {\n  console.log(\"On change state\", path());\n});\n\n// A cancellable event triggered before the history state changes.\nwindow.addEventListener(\"willchangestate\", (e: WillChangeStateEvent) =\u003e {\n  console.log(\"Before the state changes. Call 'e.preventDefault()' to prevent the state from changing.\");\n});\n\n// An event triggered when navigation starts.\nwindow.addEventListener(\"navigationstart\", (e: NavigationStartEvent) =\u003e {\n  console.log(\"Navigation start\", e.detail);\n});\n\n// An event triggered when navigation is canceled. This is due to a Route Guard returning false during navigation.\nwindow.addEventListener(\"navigationcancel\", (e: NavigationCancelEvent) =\u003e {\n  console.log(\"Navigation cancelled\", e.detail);\n});\n\n// An event triggered when navigation ends.\nwindow.addEventListener(\"navigationend\", (e: NavigationEndEvent) =\u003e {\n  console.log(\"Navigation end\", e.detail);\n});\n\n// An event triggered when navigation fails due to an unexpected error.\nwindow.addEventListener(\"navigationerror\", (e: NavigationErrorEvent) =\u003e {\n  console.log(\"Navigation failed\", e.detail);\n});\n\n// An event triggered when navigation successfully completes.\nwindow.addEventListener(\"navigationsuccess\", (e: NavigationSuccessEvent) =\u003e {\n  console.log(\"Navigation failed\", e.detail);\n});\n```\n\n#### Scroll to the top\n\nIf you want to scroll to the top on each page change to could consider doing the following.\n\n```typescript\nwindow.addEventListener(\"navigationend\", () =\u003e {\n  requestAnimationFrame(() =\u003e {\n    window.scrollTo(0, 0);\n  });\n});\n```\n\n#### Style the active link\n\nIf you want to style the active link you can do it by using the `isPathActive(...)` function along with listning to the `changestate` event.\n\n```javascript\nimport {isPathActive} from \"router-slot\";\n\nconst $links = Array.from(document.querySelectorAll(\"a\"));\nwindow.addEventListener(\"changestate\", () =\u003e {\n  for (const $link of $links) {\n\n    // Check whether the path is active\n    const isActive = isPathActive($link.getAttribute(\"href\"));\n\n    // Set the data active attribute if the path is active, otherwise remove it.\n    if (isActive) {\n      $link.setAttribute(\"data-active\", \"\");\n\n    } else {\n      $link.removeAttribute(\"data-active\");\n    }\n  }\n});\n```\n\n\r\n[![-----------------------------------------------------](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/colored.png)](#-be-careful-when-navigating-to-the-root)\r\n\r\n## ➤ ⚠️ Be careful when navigating to the root!\n\nFrom my testing I found that Chrome and Safari, when navigating, treat an empty string as url differently. As an example `history.pushState(null, null, \"\")` will navigate to the root of the website in Chrome but in Safari the path won't change. The workaround I found was to simply pass \"/\" when navigating to the root of the website instead.\n\n\r\n[![-----------------------------------------------------](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/colored.png)](#contributors)\r\n\r\n## ➤ Contributors\n\t\n\n| [\u003cimg alt=\"Andreas Mehlsen\" src=\"https://avatars1.githubusercontent.com/u/6267397?s=460\u0026v=4\" width=\"100\"\u003e](https://twitter.com/andreasmehlsen) | [\u003cimg alt=\"You?\" src=\"https://joeschmoe.io/api/v1/random\" width=\"100\"\u003e](https://github.com/andreasbm/router-slot/blob/master/CONTRIBUTING.md) |\n|:--------------------------------------------------:|:--------------------------------------------------:|\n| [Andreas Mehlsen](https://twitter.com/andreasmehlsen) | [You?](https://github.com/andreasbm/router-slot/blob/master/CONTRIBUTING.md) |\n\n\r\n[![-----------------------------------------------------](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/colored.png)](#license)\r\n\r\n## ➤ License\n\t\nLicensed under [MIT](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreasbm%2Frouter-slot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandreasbm%2Frouter-slot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreasbm%2Frouter-slot/lists"}