Skip to content

Native JavaScript navigation [web api] implementation

License

Notifications You must be signed in to change notification settings

virtualstate/navigation

Repository files navigation

@virtualstate/navigation

Native JavaScript navigation implementation

Support

Node.js supported Deno supported Bun supported Chromium supported Webkit supported Firefox supported

Test Coverage

Web Platform Tests 140/277 92.8%25 lines covered 92.8%25 statements covered 83.33%25 functions covered 82.97%25 branches covered

Install

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

importmap documentation

<script type="importmap">
    {  
        "imports": {
            "@virtualstate/navigation": "https://cdn.skypack.dev/@virtualstate/navigation"
        }
    }
</script>
<script type="module">
    import { Navigation } from "@virtualstate/navigation"
</script>

Usage

See the MDN documentation for the Navigation API for in depth information on usage.

Examples

Navigation

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;

Waiting for events

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; // Rejects

Transitions

import { Navigation } from "@virtualstate/navigation";
import { loadPhotoIntoCache } from "./cache";

const navigation = new Navigation();

navigation.addEventListener("navigate", async ({ destination, intercept }) => {
    intercept(loadPhotoIntoCache(destination.url));
});

URLPattern

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(transition());
    }

    async function transition() {
        console.log("load book", match.pathname.groups.id)
    }
});

navigation.navigate("/book/1");

State

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;

Polyfill

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 navigation.navigate("/").finished;