Stream
The <x-wirekit::stream> component renders streaming text output — an LLM response,
a live log — from a Server-Sent Events endpoint. It exists so you never re-write the
three parts that are genuinely hard to get right:
- Accessibility. A growing
aria-live="polite"field is unusable — a screen reader re-reads the whole thing on every token. Stream announces that a response is generating once, then the result once, and keeps the visible output out of the live region so it never floods. - Reduced motion. A token-by-token build is motion. Under
prefers-reduced-motion: reducethe tokens are buffered and revealed as one block. - Abort & error. A half-streamed response whose connection drops has a defined
terminal state (
aborted/failed), not a silent freeze.
You supply the URL and render the text; the library owns the state machine.
Usage
Point url at an SSE endpoint that emits text tokens and closes with a done signal
([DONE] by default). The stream starts on load and appends tokens as they arrive.
<x-wirekit::stream url="/chat/{{ $conversation->id }}/stream" />
The demos on this page use simulate — they type a fixed string out token by token
from a local timer, with no live endpoint, so you can watch the streaming behavior
right here. Use the ↻ Replay control to run it again. In your app you pass url
instead; simulate is for demos and typewriter effects.
With controls
The default slot renders inside the component's Alpine scope, so you can add Stop and
Retry controls that call stop() / restart() and react to status. Bind your
control buttons with WireKit's own components.
Announcement mode
By default (announce="result") the settled response is announced once in full. For
long outputs where reading the whole thing aloud is too much, use announce="status"
to announce only that the response is ready — the text stays on screen for the reader
to navigate to.
Seeded output (SSR / resume)
Pass initialText to render a completed response immediately — resume a finished
conversation on a server render, or show a static example — without opening a stream.
Combine it with :auto-start="false" so the component stays idle and simply displays
the seeded text.
Livewire integration
In your app, point url at a Livewire-driven route that returns text/event-stream
and writes tokens as your model produces them; drive the trigger from Livewire and let
the component own the rendering and accessibility:
{{-- 1. A route that streams tokens as Server-Sent Events, ending with [DONE]. --}}
<x-wirekit::stream url="/chat/{{ $conversation->id }}/stream" />
Props
| Prop | Type | Default | Description |
|---|---|---|---|
url |
string|null |
null |
SSE endpoint to stream from. Null → the component stays idle. |
eventName |
string |
'message' |
SSE event name to listen for. |
doneSignal |
string |
'[DONE]' |
Payload that ends the stream. |
announce |
string |
'result' |
'result' announces the final text once; 'status' announces only readiness. |
autoStart |
bool |
true |
Open the stream on init. false → start it from your own control. |
initialText |
string|null |
null |
Seed text — resume a completed response (SSR) or show a static example. |
simulate |
string|null |
null |
Stream this text token by token from a local timer, with no url — a live-looking demo or a typewriter effect. |
simulateSpeed |
int|null |
55 |
Milliseconds per token in simulate mode. |
scope |
string|null |
null |
Scoped personalization key. |
The Alpine scope exposes text, status (idle / streaming / done / aborted
/ failed), the derived isStreaming / isDone / isAborted / isFailed /
isTerminal, and the methods start(), stop(), and restart() — bind them from the
default slot for controls and custom state UIs. The error slot overrides the default
failure message.
Accessibility
- One live region. A single visually-hidden
role="status" aria-live="polite"region announces generating once, then the result (or ready) once. The visible output is deliberately not a live region, so a screen reader is never re-read on every token — the single most important decision this primitive makes. - Reduced motion. Under
prefers-reduced-motion: reduce, tokens are buffered and the full response is revealed at once — the incremental build is suppressed. The streaming caret only pulses when motion is allowed. - Defined terminal states. Abort and connection loss resolve to
aborted/failed, and the failure surfaces in arole="alert"region — never a silent hang.
Keyboard Interaction
The output itself has no interaction model — it is streamed text. Any controls you add
in the default slot (Stop, Regenerate) are ordinary buttons: Tab to reach them,
Enter / Space to activate. There is nothing bespoke to learn.
Design Tokens
| Token | Used for |
|---|---|
--text-wk-md / --color-wk-text / --font-wk-sans |
Streamed output type |
--color-wk-text-muted |
Streaming caret |
--color-wk-danger-text |
Failure message |
--gap-wk-xs |
Vertical rhythm between output, caret, and controls |