For AI agents: fetch /llms.txt first for the curated documentation index, then use same-host Markdown pages when available.

Concepts

Fragment Navigation

The router upgrades native links into streamed HTML navigation. It returns explicit navigate, prefetch, and invalidate capabilities and can be torn down with an AbortSignal.

Start once

// client/index.js
import "@nativefragments/lit/client";
import { startRouter } from "@nativefragments/core/client/router.js";

const lifetime = new AbortController();
export const router = startRouter({
  target: "#content-slot",
  prefetch: "intent",
  viewTransitions: true,
  signal: lifetime.signal,
});

There is one active router per document. Aborting the lifetime signal removes listeners, disconnects observers, cancels in-flight requests, and permits another router to start.

<a href="/reports">Reports</a>
<a href="/settings" data-fragment-prefetch="visible">Settings</a>
<a href="/export.csv" data-nativefragments-reload>Export</a>

Same-origin application links are upgraded. External URLs, downloads, modified clicks, document-like assets, explicit reload links, and opted out links retain browser navigation.

Named targets

Use a named fragment when one route owns a smaller independently navigable region. The server and HTML share the same slot name.

const panel = fragment("settings-panel", renderSettings);

route("/settings/profile", {
  render: () => html`
    <a href="/settings/profile"${panel.prefetchAttrs("intent")}>Profile</a>
    <section${panel.attrs()}>${renderSettings()}</section>
  `,
  fragments: [panel],
});

Different targets may navigate concurrently. A newer navigation to the same target supersedes the older consumer without cancelling shared prefetched work used elsewhere.

Imperative capabilities

await router.navigate("/reports", { history: "push" });
await router.navigate("/settings/profile", { slot: "settings-panel" });
await router.prefetch("/reports");
router.invalidate("/reports");
router.invalidate(); // all cached and in-flight fragments

navigate and prefetch accept a consumer AbortSignal. invalidate is explicit; mutations decide which cached HTML became stale.

GET forms

<form action="/search" method="get" data-fragment-form>
  <input name="q" />
  <button>Search</button>
</form>

Opted-in GET forms navigate as fragments. POST forms remain native and use route actions plus redirects, preserving a reliable no-JavaScript mutation path.

Lifecycle events

document.addEventListener("nativefragments:navigation-complete", (event) => {
  console.log(event.detail.url, event.detail.target);
});

// navigation-start → navigation-swap → fragment-reveal* → navigation-complete
// navigation-start → navigation-abort | navigation-error

Events bubble and cross shadow boundaries. During a request the target exposes aria-busy="true" and a data-nativefragments-navigation state.

Protocol negotiation

Router requests send X-NativeFragments-Protocol: 1. A compatible server may return a framed stream and echoes the version. Missing or unknown versions receive one completed buffered fragment, so an old tab cannot mistake stream frames for one HTML document.

See also