---
title: Replay Button
description: Companion button that re-mounts the closest [data-replay-target] ancestor — pairs with the data-replayable contract emitted by re-runnable and resettable WireKit previews.
visibility: guest
draft: false
related:
  - /components/reveal
  - /components/stat
---

# Replay Button

Re-runs (or resets) a demo by replacing the wrapping element's inner HTML with a saved snapshot, then calling `Alpine.initTree()` so every Alpine scope re-binds. Pairs with the `data-replayable="true"` attribute emitted by any WireKit preview that can be re-run or reset — animation-capable components (`<x-wirekit::reveal>`, `<x-wirekit::stat animate>`, animated card / hero / cta / feature / footer, chart) and state-mutating components whose demo is "used up" by interaction (a dismissible `<x-wirekit::badge>` or `<x-wirekit::alert>`).

::: info
**Why this component exists:** it primarily powers WireKit's own documentation site — docs.wirekit.app wraps every re-runnable example in the replay pattern and renders a `↻ Replay` control on top. Since it ships in the package regardless, it doubles as a ready-made building block for your own app: wire it up to re-run an animation, reset an interactive demo to its starting state, or restore a dismissed badge or alert. Combine it with the `data-replayable` contract below and use it however fits your UI.
:::

## How it works

The pattern requires three pieces:

1. A wrapper element carrying `data-replay-target` and `data-replay-source="<encoded snapshot>"`.
2. Inside that wrapper: the actual demo (`<x-wirekit::reveal>`, `<x-wirekit::stat animate>`, etc.) which emits `data-replayable="true"`.
3. The `<x-wirekit::replay-button>` itself, anywhere in the same DOM tree.

On click, the button walks `closest('[data-replay-target]')` from itself, replaces that element's `innerHTML` with the snapshot, then calls `Alpine.initTree(root)` to re-mount.

The docs site auto-injects this pattern around every `:::preview` block whose source matches a `data-replayable` shape, so developers reading the docs see a "↻ Replay" button on every re-runnable or resettable example — an animated demo, or a dismissible badge / alert whose chips can be restored after they are dismissed.

## Usage

The simplest shape — drop an animated demo into a `:::preview` block and the docs site auto-wires the replay-target wrapper around it:

:::preview{title="Replay-aware reveal animation"}
<x-wirekit::reveal preset="fade" delay="lg" duration="slow">Hello!</x-wirekit::reveal>
:::

For developers wiring the pattern manually in their own app (outside the docs auto-injection), the full shape is:

```blade
<div data-replay-target
     data-replay-source="{{ e('<x-wirekit::reveal preset=\'fade\'>Hello!</x-wirekit::reveal>') }}">
    <x-wirekit::reveal preset="fade">Hello!</x-wirekit::reveal>
</div>
<x-wirekit::replay-button />
```

The `data-replay-source` attribute MUST hold an HTML-escaped snapshot of the inner markup — the click handler decodes it and assigns it to `innerHTML` before re-running `Alpine.initTree()`. Use `e(...)` or Blade's `{{ }}` echo to escape angle brackets and quotes, otherwise the browser parses the snapshot prematurely and the replay restores broken HTML.

The button picks up the closest `data-replay-target` ancestor automatically. To target a specific selector instead, pass `target="…"`:

```blade
<x-wirekit::replay-button target="[data-demo-root]" />
```

## Customizing the icon

Pass slot content to override the default circular-arrow glyph:

```blade
<x-wirekit::replay-button>
    <x-wirekit::icon name="refresh" size="sm" />
    Replay
</x-wirekit::replay-button>
```

## Style hooks

Single BEM root: `wk-replay-button`. Override via your `app.css`:

```css
.wk-replay-button {
    color: var(--color-wk-text-subtle);
    transition: color var(--motion-wk-duration-md);
}

.wk-replay-button:hover {
    color: var(--color-wk-text);
}
```

## Props

| Prop | Type | Default | Notes |
|------|------|---------|-------|
| `label` | `string` | `'Replay'` | Becomes `aria-label` on the rendered `<button>`. |
| `target` | `string\|null` | `null` | Optional CSS selector; when set, the click handler walks `$el.closest(selector)` instead of `[data-replay-target]`. |

## Accessibility

- Default `aria-label="Replay"` (overridable via the `label` prop).
- Renders as a real `<button type="button">` so keyboard activation (Space / Enter) works without extra wiring.
- Dispatches `wirekit:replayed` (bubbling) on the `data-replay-target` root after re-mount; listeners can hook into the event for analytics / focus management.

## Keyboard Interaction

| Key | Action |
|-----|--------|
| `Tab` | Move focus to / away from the button (native browser tab order). |
| `Space` | Activate the replay (re-mount the demo). |
| `Enter` | Activate the replay (re-mount the demo). |

Focus stays on the button after activation so a keyboard user can replay multiple times without needing to re-tab. The dispatched `wirekit:replayed` event fires after the inner DOM is replaced; developers can listen to it and shift focus elsewhere if the demo's first interactive element should receive focus instead.
