# routable-element

URL matching for custom elements — give any Neutron class a
`route-href` and react when the address bar changes.

## Features

- **Path patterns** Named params (`/users/:id`) and wildcards
- **Regex routes** Full control via `route-regex` (ignored when `route-href` is set); named groups (`(?<id>\d+)`) become `params`, in a path pattern too, unnamed ones stay in `match`
- **Nested match** Keep layouts active under child paths
- **Live updates** Rematch when `route-href` / `route-regex` change

## Installation


`@excom/routable-element` v0.2.2

```bash
pnpm add @excom/routable-element
```

```bash
npm install @excom/routable-element
```

```bash
yarn add @excom/routable-element
```

### Import

```ts
import { /* … */ } from "@excom/routable-element";
```



## Usage

Compose `RoutableElement` and implement `routeChanged`. Concrete
consumers include `<spa-route>`, `<spa-a>`, and `<spa-manager>`.

```ts
import { Neutron } from "@excom/neutron";
import { RoutableElement } from "@excom/routable-element";

export const PathAware = Neutron.compose([
  RoutableElement,
  Neutron({
    tag: "path-aware",
    props: {
      isActive: Boolean,
    },
  }),
])
  .defineMethods({
    routeChanged: (_el, { match }) => ({ isActive: !!match }),
  });

PathAware.define();
```

```html
<path-aware route-href="/dashboard" match-nested></path-aware>
```

### API Reference


#### Attributes

| Name | Surface | Type | Default | Values | Description |
| --- | --- | --- | --- | --- | --- |
| `route-href` | option | `string` |  | `<path pattern>` | URL pattern to match. Supports named placeholders (`/users/:id`) and wildcards: `*rest` matches one path segment, a bare `*` across segments. A relative pattern (`checkout`) resolves against the page's `<base>`; without one, against the page URL at registration. Set this or `route-regex`; with both, this one wins. |
| `route-regex` | option | `string` |  | `<RegExp source>` | RegExp source string matched against the pathname (never the query or hash) — alternative to `route-href` for catch-alls / advanced patterns, ignored when `route-href` is set. Here and in a `route-href`, named groups become `params` (`(?<id>\d+)` → `params.id`); unnamed groups stay in `match`. |
| `match-nested` | option | `boolean` |  |  | Also match child paths of `route-href`: `/users` matches `/users` and `/users/42`, never `/usersx`. Essential for layout routes and nested SPAs. |



### Examples

#### Path match & nested layouts

`route-href` activates on exact match; with `match-nested` it also matches child paths, keeping a layout mounted.

```html
<spa-route route-href="/users" match-nested>
  <template>…layout…</template>
</spa-route>
```

#### Catch-all via regex

```html
<spa-route route-regex=".*" is-fallback>
  <template>404</template>
</spa-route>
```

## Release notes

### 0.2.0 (2026-09-30)

- Update `match-nested` to match its own path as well as child paths: `route-href="/users"` is active at `/users`, so a separate route for that path renders beside it and a 404 fallback stays off there
- Add relative `route-href` values: `checkout` resolves against the page's `<base>`, or against the page URL at registration when there is none
- Fix an issue where a routable element inside `persist-content` threw a `NeutronError` when re-attached
- Fix an issue where an element given a `route-href` while detached registered a route, and a `route-regex` element stayed registered after it was removed
- Fix an issue where changing `match-nested` at runtime had no effect

### 0.1.1 (2026-09-23)

- Ship only dist (and declared extras) in the npm tarball; drop build logs, tests and sources
