# lit-ui-router

<img src="https://lit-ui-router.dev/images/lit-ui-router.svg" alt="Lit UI Router" width="120" height="120" />

[![npm version](https://img.shields.io/npm/v/lit-ui-router.svg)](https://npmx.dev/package/lit-ui-router)
[![GitHub Release](https://img.shields.io/github/v/release/simshanith/lit-ui-router?filter=lit-ui-router@*)](https://github.com/simshanith/lit-ui-router/releases/?q=lit-ui-router)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Website](https://img.shields.io/website?url=https%3A%2F%2Flit-ui-router.dev)](https://lit-ui-router.dev)
[![codecov](https://codecov.io/gh/simshanith/lit-ui-router/graph/badge.svg?component=lit-ui-router)](https://app.codecov.io/gh/simshanith/lit-ui-router?components%5B0%5D=lit-ui-router)

A [UI-Router](https://ui-router.github.io/) implementation for [Lit](https://lit.dev/).

## Quick Start

```ts
import { UIRouterLit, LitStateDeclaration } from 'lit-ui-router';
import { hashLocationPlugin } from '@uirouter/core';
import { html } from 'lit';

const router = new UIRouterLit();
router.plugin(hashLocationPlugin);

const states: LitStateDeclaration[] = [
  { name: 'home', url: '/', component: () => html`<h1>Home</h1>` },
  { name: 'about', url: '/about', component: () => html`<h1>About</h1>` },
];
states.forEach((state) => router.stateRegistry.register(state));

router.start();
```

## Entry Points

| Import                                        | Effect                                                                                                                                                                                                                                      |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `import { ... } from 'lit-ui-router'`         | The router, the elements, the directives and the controllers. Any value import registers the `<ui-router>`/`<ui-view>` custom elements as a side effect.                                                                                    |
| `import { ... } from 'lit-ui-router/pure'`    | The same API — element classes included — with no registration and no `HTMLElementTagNameMap` globals.                                                                                                                                      |
| `import 'lit-ui-router/register'`             | Registration only: defines `<ui-router>`/`<ui-view>` and carries their `HTMLElementTagNameMap` entries.                                                                                                                                     |
| `import 'lit-ui-router/ui-view.register'`     | Single-element registration: defines just that element with its tag-map entry (`ui-router.register` ditto).                                                                                                                                 |
| `import { ... } from 'lit-ui-router/context'` | The only home of the [`context-request`](https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md) key, event and request helper, and of the tree-less router hand-off — no elements, and nothing from `lit`. |
| `import type { ... } from 'lit-ui-router'`    | Types are erased at compile time — always free, from any entry.                                                                                                                                                                             |

The root entry is exactly `pure` + `register`: reach for `lit-ui-router/pure`
when you need the APIs (or the element classes themselves, e.g. for scoped
registries or custom tag names) without touching the global registry, and pair
it with `lit-ui-router/register` — or a single `*.register` entry — to opt
into registration explicitly.

## Development and Production Builds

Every entry point above publishes twice: `dist/development/*.js` alongside `dist/*.js`, picked by the `development` export condition, which bundlers resolve automatically. Nothing to configure.

The development build carries three console warnings — `uiSref` writing an inert `href`, `uiSrefActive` taking over an `aria-current` it did not set, and any binding finding no `<ui-router>` ancestor. Each is gated on lit's own development build as well, and none of them, message text included, is present in a production bundle. See the [Development & Production Builds guide](https://lit-ui-router.dev/guides/development-builds) for what each warning means and how the split is verified.

## Component Styles

| Style               | Best For                      | Example                    |
| ------------------- | ----------------------------- | -------------------------- |
| Template function   | Simple views, prototyping     | `` () => html`...` ``      |
| Template with props | Views needing params/resolves | `` (props) => html`...` `` |
| LitElement class    | Complex views with lifecycle  | `MyComponent`              |

## Reacting to Transitions

Any element (routed or not) can stay synchronized with the router using the
`TransitionController` reactive controller. It registers transition hooks when
the host connects, calls `host.requestUpdate()` on matching transitions, and
deregisters everything when the host disconnects — no manual `requestUpdate()`
plumbing or leaked hooks.

```ts
import { TransitionController } from 'lit-ui-router';

class NavHeader extends LitElement {
  private transitions = new TransitionController(this);

  render() {
    // Re-evaluated after every successful transition
    return html`Current state: ${this.transitions.current?.name}`;
  }
}
```

Scope it to specific states or react to parameter changes with a callback:

```ts
class UserDetail extends LitElement {
  private transitions = new TransitionController(this, {
    criteria: { to: 'users.detail' },
    callback: () => this.loadUser(this.transitions.params.userId),
  });
}
```

## Navigation Links

`uiSref` and `uiSrefActive` bind a link from the element; `srefHref`, `srefActiveClass` and `srefAriaCurrent` bind the same link from inside the attribute it writes, so the `href` is real markup a static analyser or server renderer can read — see [Attribute-part forms](https://lit-ui-router.dev/api/#attribute-part-forms).

## Documentation

Visit [lit-ui-router.dev](https://lit-ui-router.dev) for full documentation, tutorials, and API reference.
