Native JavaScript navigation implementation
npm / yarn / GitHub
npm i --save @virtualstate/navigation
Or
yarn add @virtualstate/navigation
Then
import { Navigation } from "@virtualstate/navigation";Skypack
const { Navigation } = await import("https://cdn.skypack.dev/@virtualstate/navigation");Or
import { Navigation } from "https://cdn.skypack.dev/@virtualstate/navigation";importmap
<script type="importmap">
    {  
        "imports": {
            "@virtualstate/navigation": "https://cdn.skypack.dev/@virtualstate/navigation"
        }
    }
</script>
<script type="module">
    import { Navigation } from "@virtualstate/navigation"
</script>See the MDN documentation for the Navigation API for in depth information on usage.
Examples
import { Navigation } from "@virtualstate/navigation";
const navigation = new Navigation();
// Set initial url
navigation.navigate("/");
navigation.navigate("/skipped");
// Use .finished to wait for the transition to complete
await navigation.navigate("/awaited").finished;import { Navigation } from "@virtualstate/navigation";
const navigation = new Navigation();
navigation.addEventListener("navigate", async ({ destination, preventDefault }) => {
    if (new URL(destination.url).pathname === "/disallow") {
        preventDefault();
    }
});
await navigation.navigate("/allowed").finished; // Resolves
await navigation.navigate("/disallow").finished; // Rejectsimport { Navigation } from "@virtualstate/navigation";
import { loadPhotoIntoCache } from "./cache";
const navigation = new Navigation();
navigation.addEventListener("navigate", async ({ destination, intercept }) => {
    intercept({
        async handler() {
            await loadPhotoIntoCache(destination.url)
        }
    });
});You can match destination.url using URLPattern
import {Navigation} from "@virtualstate/navigation";
import {URLPattern} from "urlpattern-polyfill";
const navigation = new Navigation();
navigation.addEventListener("navigate", async ({destination, intercept}) => {
    const pattern = new URLPattern({ pathname: "/books/:id" });
    const match = pattern.exec(destination.url);
    if (match) {
        intercept({
            handler: transition
        });
    }
    async function transition() {
        console.log("load book", match.pathname.groups.id)
    }
});
navigation.navigate("/book/1");import { Navigation } from "@virtualstate/navigation";
const navigation = new Navigation();
navigation.addEventListener("currententrychange", () => {
    console.log({ updatedState: navigation.currentEntry?.getState() });
});
await navigation.updateCurrentEntry({
    state: {
        items: [
            "first",
            "second"
        ],
        index: 0
    }
}).finished;
await navigation.updateCurrentEntry({
    state: {
        ...navigation.currentEntry.getState(),
        index: 1
    }
}).finished;If a global instance of the navigation API is not available, this will provide one, integrated into the History API if available.
import "@virtualstate/navigation/polyfill";
await window.navigation.navigate("/").finished;
// Or if within a window global scope, aka in a browser:
await navigation.navigate("/").finished;Polyfill Global Window Types
See @types/dom-navigation for a standardised type definition for the Navigation API
which can be utilised alongside this polyfill.
yarn add --dev @types/dom-navigationThis should then be included as a type in your tsconfig.json:
{
  "compilerOptions": {
    "types": [
      "dom-navigation"
    ]
  }
}Polyfill Serializer
You may want to set a custom serializer to store state in history
The default serializer is JSON
In the past, a structured clone like serializer was used. This may be useful for you if you're using native types rather than just JSON compatible values.
An example of making use of a custom serializer with the polyfill:
import { setSerializer } from "@virtualstate/navigation/polyfill";
import { serialize, deserialize } from "@ungap/structured-clone";
setSerializer({
    stringify(value) {
        return serialize(value)
    },
    parse(value) {
        return deserialize(value)
    }
});- Navigation by setting window.location does not trigger navigate event
- Normal page navigations are not intercepted (excluding navigation api & history made changes)
Please create a new GitHub Issue if you find further limitations