---
title: Branch Switcher
description: Step between several generated answers — prev/next, a live count, and a polite announcement per move
visibility: guest
draft: false
---

# Branch Switcher

When a model regenerates an answer, the earlier one is not waste — it is the other option. The
branch switcher is how a reader moves between them: two arrows, a count, and an announcement
that says the whole sentence rather than a fragment.

It is a **control**, not a container. You render whichever variant is active; the switcher owns
the index and hands it back.

## Basic Usage

:::preview{title="Two arrows and a count"}
<x-wirekit::branch-switcher :count="3" :current="2" />
:::

The index is **1-based** everywhere — in the label the reader sees, in the value `wire:model`
hands the server, and in the `current` prop you write. One numbering, no conversions.

## Wiring It to Livewire

`wire:model` reaches the index itself. Bind it, render the active variant, and the reader's move
arrives as an ordinary property update.

The component holds the index and the answers:

```php
<?php

namespace App\Livewire;

use Livewire\Component;

class AnswerVariants extends Component
{
    // 1-based, like the switcher: 1 is the first answer.
    public int $branch = 1;

    // One entry per generated answer. A regeneration appends to it.
    public array $answers = [
        'Sixty requests a minute, per token.',
        'Sixty a minute per token, and the 429 says how long to wait.',
    ];
}
```

The view binds the switcher and renders the answer it points at:

```blade
{{-- 1. The switcher owns which one is showing; you own what that means. --}}
<x-wirekit::branch-switcher
    wire:model.live="branch"
    :count="count($this->answers)"
    :current="$branch"
/>

{{-- 2. Render the active variant. The index is 1-based, so subtract one for a zero-based list. --}}
<x-wirekit::assistant-message model="atlas-2">
    {{ $this->answers[$branch - 1] }}
</x-wirekit::assistant-message>
```

Without Livewire, listen for the event instead:

```js
// 1. Bubbles, so one listener on the conversation covers every turn in it.
document.addEventListener('wirekit:branch-switcher:change', (event) => {
    // 2. `detail` carries both numbers, so a handler never has to re-read the DOM.
    showVariant(event.detail.current, event.detail.total);
});
```

## Under an Answer

The switcher belongs where the reader looks after reading — under the turn, beside the other
per-answer controls.

:::preview{title="An answer with its variants underneath"}
<x-wirekit::assistant-message model="atlas-2">
    Sixty requests a minute, per token. The 429 carries a <code>Retry-After</code> header.

    <x-slot:actions>
        <x-wirekit::branch-switcher :count="3" :current="1" label="Answer variants" />
    </x-slot:actions>
</x-wirekit::assistant-message>
:::

## The Ends Do Not Wrap

A reader stepping through variants expects the last one to be the last one. Silent wrap-around
loses that, so the arrows disable at the ends instead.

Pass `loop` where cycling is what you want — a carousel of drafts rather than a sequence.

## Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `count` | `int` | `0` | How many variants there are. `0` renders nothing at all. |
| `current` | `int` | `1` | Which one is showing, 1-based. Out of range is clamped, never refused. |
| `label` | `string` | `null` | Names the group. Falls back to a translated default. |
| `loop` | `bool` | `false` | Whether the ends wrap. |
| `scope` | `string` | `null` | Personalization scope, as on every component. |

## Events

| Event | Detail | When |
|---|---|---|
| `wirekit:branch-switcher:change` | `{ current, total }` | A move settled. Bubbles. |

## Accessibility

- The group carries a **name**. An unset `label` falls back to a translated default rather than
  to silence, because a group of buttons with no name is announced as a group of nothing.
- Each move announces the **whole sentence** — "Showing response 2 of 3" — into a polite region
  that exists before it has anything to say. A live region created together with its text is
  inert, and a fragment appended to an existing one tells a screen reader's user nothing.
- The count beside the arrows is a plain label, **not** a live region. Making it live would
  interrupt the reader mid-move, and the announcer already speaks once the move has settled.
- <kbd>←</kbd> and <kbd>→</kbd> work while either arrow has focus, so the keyboard reaches the
  whole control without leaving it.
- Both arrows meet the touch-target floor the rest of the library uses.

## Keyboard Interaction

| Key | Action |
|---|---|
| <kbd>←</kbd> | Previous variant. |
| <kbd>→</kbd> | Next variant. |
| <kbd>Enter</kbd> / <kbd>Space</kbd> | Activates the focused arrow. |

## See Also

- [Assistant Message](/components/assistant-message) — the turn the variants belong to.
- [Tool Call](/components/tool-call) — what the assistant did before it answered.
- [Carousel](/components/carousel) — for stepping through content rather than through answers.
