Skip to main content

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 call onLoad, 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 Templatemain.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

FunctionWhen 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

FunctionPurpose
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:

HookReturns
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:

APIUse inPurpose
registerBinding(Event.X, handler)coreRun a handler each time a moment event fires.
useEvent(Event.X, handler)templateSame, as a hook.
useLatestEvent(Event.X)templateThe most recent payload for an event (or a live value's current value).
useEventLog(Event.X, limit?)templateThe last N occurrences — great for a chat or event feed.
useBinding(path, selector?)templateRead a specific live value by path, with an optional selector.

The full list of events and their payloads is in Event bindings.

Assets & audio

APIPurpose
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:

HookPurpose
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:

APIPurpose
ChatMessageA ready-made component that renders a chat message with emotes and badges.
ChatBadgesRenders 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:

ComponentPurpose
TextStyled text with outlines, anchoring and colour fills.
ImageAn image with fit, radius and other display options.
ProgressBarA configurable fill bar.

Utilities

APIPurpose
formatNumber/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_VERSIONThe runtime API version your widget is running against.
Render untrusted text safely

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:

APIPurpose
useChildren() / childrenThe 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.
BindA 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