The @widget runtime
Everything your widget needs is imported from the @widget module:
import { onLoad, showTemplate, registerBinding, Event, props, Template } from '@widget';
This page is a reference. If you're just starting, follow Build your first widget first.
The mental model
A widget has two kinds of code:
- The core (
.ts) — plain code that runs once when the widget loads. This is where you callonLoad, choose which template to show, and register your event bindings. The core is not React. - Templates (
.tsx) — React components that draw the widget. Inside a template you use hooks (useProp,useLatestEvent, …) that re-render when their value changes.
Each template file becomes a member of Template — main.tsx is Template.Main, crash.tsx is Template.Crash.
A minimal widget:
// main.ts (the core)
import { onLoad, showTemplate, setCrashTemplate, Template } from '@widget';
onLoad(() => {
setCrashTemplate(Template.Crash); // shown if a template throws
showTemplate(Template.Main); // paint the Main template
});
// main.tsx (a template)
import { useProp } from '@widget';
export default function Main() {
const text = useProp('message');
return <h1>{text}</h1>;
}
Lifecycle
| Function | When it runs |
|---|---|
onLoad(handler) | Once, when the widget starts. The handler receives a context with the current binding snapshot, the configured props, and the canvas size. |
onUpdate(handler) | Whenever the streamer changes a property value. |
onResize(handler) | When the widget's size changes. |
Templates
| Function | Purpose |
|---|---|
showTemplate(Template.Name, props?) | Paint a template to the live overlay and the editor preview. |
showEditorTemplate(Template.Name, props?) | Paint a template only on the Overlay Editor design canvas (use it to show sample data while a streamer designs). |
setCrashTemplate(Template.Name) | A fallback template shown if your main template throws — so a bug shows a graceful placeholder instead of nothing. |
Reading configuration & state
In the core, read configured properties off the props object:
import { props } from '@widget';
const colour = props.accentColour;
In a template, use hooks so the UI updates when values change:
| Hook | Returns |
|---|---|
useProp(key) | A configured property value. |
useSize() | The widget's current { width, height }. |
useStore(key) | A value from your widget's saved storage. |
useCounter(name) | A named counter you can increment across sessions. |
useReducedMotion() | true if the viewer prefers reduced motion — honour it for accessibility. |
See Property schema for how properties are defined and named.
Events
Declare events on the Bindings surface, then consume them:
| API | Use in | Purpose |
|---|---|---|
registerBinding(Event.X, handler) | core | Run a handler each time a moment event fires. |
useEvent(Event.X, handler) | template | Same, as a hook. |
useLatestEvent(Event.X) | template | The most recent payload for an event (or a live value's current value). |
useEventLog(Event.X, limit?) | template | The last N occurrences — great for a chat or event feed. |
useBinding(path, selector?) | template | Read a specific live value by path, with an optional selector. |
The full list of events and their payloads is in Event bindings.
Assets & audio
| API | Purpose |
|---|---|
asset('name') | The URL of an asset you uploaded on the Assets surface. |
playSound('name', options?) | Play an uploaded sound; returns a handle you can stop. |
import { asset } from '@widget';
export default function Main() {
return <img src={asset('logo')} alt="" />;
}
Time & animation
These hooks make smooth, time-based widgets easy:
| Hook | Purpose |
|---|---|
useNow(intervalMs?) | The current time, ticking — for clocks. |
useElapsed(since, intervalMs?) | Seconds elapsed since a timestamp — for uptime. |
useCountdown(until, intervalMs?) | Seconds remaining until a timestamp — for ad/goal timers. |
useTween(target, options?) | A value that eases toward target — smooth number transitions. |
useRaf(callback) | A per-frame callback for custom animation. |
useInterval(fn, ms) / useTimeout(fn, ms) | Cleanup-safe timers. |
usePresence() | The widget's enter/active/leave phase, for entrance/exit animations. |
Chat & emotes
Building a chat widget is well-supported:
| API | Purpose |
|---|---|
ChatMessage | A ready-made component that renders a chat message with emotes and badges. |
ChatBadges | Renders a chat author's badges. |
parseChatMessage(chat, emotes?, opts?) | Tokenise a message into text / emote / mention / link runs for custom rendering. |
useChannelEmotes() | The channel's current emote sets (Twitch + 7TV + BTTV + FFZ). |
pickEmoteUrl(urls, targetPx) / pickBadgeUrl(badge, size) | Choose the best-resolution image for a size. |
Built-in components
The runtime ships a few components so you don't reinvent the basics:
| Component | Purpose |
|---|---|
Text | Styled text with outlines, anchoring and colour fills. |
Image | An image with fit, radius and other display options. |
ProgressBar | A configurable fill bar. |
Utilities
| API | Purpose |
|---|---|
format | Number/time formatting helpers. |
band(value, breakpoints, fallback) | Map a number into one of several buckets (e.g. small/medium/large). |
escapeHtml(text) / sanitizeHtml(html) | Make untrusted text safe to render. |
RUNTIME_VERSION | The runtime API version your widget is running against. |
Chat messages, user input and viewer names come from the public. If you ever insert them as HTML, run them through sanitizeHtml (or just render them as text and let React escape them). Never trust raw input.
Advanced: container widgets
A widget can be a container that holds other widgets. Containers use a few extra APIs:
| API | Purpose |
|---|---|
useChildren() / children | The child widgets, so the container can position or animate them. |
setSubtreeContext(next) | Publish live values to nested children (they read them via the variable picker). |
useSubtreeContext(path?) | Read a value published by an ancestor container. |
Bind | A component that displays a bound value inside a template. |
Containers are how the built-in Alert, Group and Show / Hide primitives work. They're an advanced topic — start with leaf widgets.
Next
- Publishing & sharing — get your widget out to others.
- Build your first widget — apply all of this end to end.