# States

**`sonic-states`** renders the child `<template>` whose `data-value` matches a value of its data provider. Use it to switch views on a status or a selection; point `data-path` at the value, or set `state` directly.

## Basic example

Each `data-value` is a RegExp tested against the value. All matching templates render; a template with `data-value=""` renders while the value is empty.

<sonic-code>
  <template>
    <div class="flex flex-wrap gap-2" formDataProvider="states-flight-status">
      <sonic-button radio name="status" value="boarding" size="xs">Boarding</sonic-button>
      <sonic-button radio name="status" value="delayed" size="xs">Delayed</sonic-button>
      <sonic-button radio name="status" value="landed" size="xs">Landed</sonic-button>
    </div>
    <div class="my-4 rounded border p-4 text-center text-2xl text-neutral-700">
      <sonic-states dataProvider="states-flight-status" data-path="status">
        <template data-value="">Select a status</template>
        <template data-value="^boarding$">CN 001 is boarding at gate B12</template>
        <template data-value="^delayed$">CN 001 is delayed by 45 minutes</template>
        <template data-value="^landed$">CN 001 has landed at JFK</template>
      </sonic-states>
    </div>
  </template>
</sonic-code>

Templates without `data-value` are ignored, and nothing renders when no template matches.

## Captured values

Add `dataProviderExpression` to wrap the template content in an element whose `dataProvider` is built from the value: `$1` · `$2`… for RegExp groups. Children such as [sonic-fetch](/core/components/functional/fetch) then read that provider.

<sonic-code>
  <template>
    <div serviceURL="/docs-mock-api">
      <div class="flex flex-wrap gap-2" formDataProvider="states-flight-pick">
        <sonic-button radio name="flight" value="flight_001" size="xs">CN 001</sonic-button>
        <sonic-button radio name="flight" value="flight_102" size="xs">CN 102</sonic-button>
        <sonic-button radio name="flight" value="flight_305" size="xs">CN 305</sonic-button>
      </div>
      <sonic-states dataProvider="states-flight-pick" data-path="flight">
        <template data-value="flight_(\d+)" dataProviderExpression="flights?q=$1">
          <sonic-fetch key="flights.0"></sonic-fetch>
          <p class="my-4">
            <sonic-value key="code"></sonic-value> to
            <sonic-value key="city"></sonic-value>:
            <sonic-value key="status"></sonic-value>
          </p>
        </template>
      </sonic-states>
    </div>
  </template>
</sonic-code>

With [url-pattern](https://www.npmjs.com/package/url-pattern) syntax, name the segments instead: `data-value="flight/:code"` and `dataProviderExpression="flights?q=:code"`.

## Inverted matching

With `inverted`, the value is the RegExp and each `data-value` is the tested text. Use it to show several templates at once.

<sonic-code>
  <template>
    <div class="flex flex-wrap gap-2" formDataProvider="states-board-filter">
      <sonic-button radio name="filter" value=".*" size="xs">All</sonic-button>
      <sonic-button radio name="filter" value="delayed|cancelled" size="xs">Disrupted</sonic-button>
      <sonic-button radio name="filter" value="on-time" size="xs">On time</sonic-button>
    </div>
    <sonic-states inverted dataProvider="states-board-filter" data-path="filter" class="my-4 flex flex-col gap-1">
      <template data-value="on-time"><p>CN 014 · LHR · on time</p></template>
      <template data-value="delayed"><p>CN 102 · GIG · delayed</p></template>
      <template data-value="cancelled"><p>CN 305 · IAD · cancelled</p></template>
    </sonic-states>
  </template>
</sonic-code>

While the value is empty, every template renders.

## Render functions

From Lit, pass a `.states` map instead of templates: keys are RegExp or url-pattern expressions, values are render functions. RegExp groups arrive as an array, url-pattern segments as an object, and `fallback` renders when no key matches.

<sonic-code language="typescript">
  <template>
private states = {
  boarding: () => html`&lt;p&gt;Boarding at gate B12&lt;/p&gt;`,
  "delayed_(\\d+)": ([minutes]) => html`&lt;p&gt;Delayed by ${minutes} min&lt;/p&gt;`,
  "flights/:code/:status": ({ code, status }) => html`&lt;p&gt;${code}: ${status}&lt;/p&gt;`,
  fallback: () => html`&lt;p&gt;Unknown status&lt;/p&gt;`,
};

html`&lt;sonic-states dataProvider="flight" data-path="status" .states=${this.states}&gt;&lt;/sonic-states&gt;`;
  </template>
</sonic-code>

## API reference

| Attribute | Values | Description |
|-----------|--------|-------------|
| `dataProvider` | string | Provider holding the value. Inherited from the closest ancestor when omitted |
| `data-path` | string | Dot path of the value in the provider |
| `state` | string | Value to match when `data-path` is not set |
| `inverted` | boolean (default `false`) | Uses the value as the RegExp and each `data-value` as the tested text |
| `states` | object (property) | Render functions keyed by expression, plus `fallback`. Templates are used only when nothing in the map renders |

| Template attribute | Description |
|--------------------|-------------|
| `data-value` | RegExp or url-pattern matched against the value. `""` matches an empty value |
| `dataProviderExpression` | Builds the `dataProvider` of a wrapper around the template content from `$1`… or `:name` |

| Slot | Description |
|------|-------------|
| *(default)* | `<template data-value>` children |
