A floating launcher that expands into a live call panel. One component, no layout work.
AvatarWidget is the drop-in site assistant: a bubble docked to a corner that
expands into a panel and starts a call. It is position: fixed and sizes itself,
so unlike Avatar it needs no sized parent — you drop it
in and it places itself.
Three things the prop list does not tell you. It renders its own <Avatar>
into the panel — do not add one. It reads the session through useAvatar(),
so it only works inside an AvatarProvider. And the stylesheet is not optional
here: the widget's box is built from CSS custom properties in
@avatarfactory/react/styles.css, so without that import it has no size at all.
Mounting it fetches the avatar config and the .riv file so the bubble can idle
the real character. That is all. getSessionToken is not called and nothing is
billed until the widget is opened and the session starts.
Every field is optional and defaulted, so <AvatarWidget /> with no appearance
renders correctly.
| Prop | Type | Default | Description |
|---|---|---|---|
appearance.positionOptional | WidgetPosition | "bottom-right" | Corner the widget docks to: bottom-right, bottom-left, top-right, top-left. Corners only — a launcher does not live in the middle of a page. |
appearance.offsetOptional | { x: number; y: number } | { x: 20, y: 20 } | Distance in pixels from the docked corner. |
appearance.bubbleSizeOptional | number | 64 | Diameter of the collapsed launcher, in pixels. |
appearance.panelOptional | { w: number; h: number } | { w: 384, h: 560 } | Size of the expanded panel, in pixels. Ignored on mobile when mobile is set to sheet. |
appearance.accentOptional | string | No default | Ring, controls, and focus colour. Falls back to a tone derived from the avatar. |
appearance.panelBackgroundOptional | string | "#F4F2EE" | Panel backdrop once expanded. The collapsed launcher stays transparent so the character composites onto your page. Pass a hex colour — the widget picks readable foreground ink from its luminance, and anything that is not hex keeps the dark default. |
appearance.avatarScaleOptional | number | 0.6 | Fraction of the panel the character fills, 0 to 1. Below 1 it is inset so gestures reaching outside the body are not clipped. |
appearance.draggableOptional | boolean | false | Let the user drag the widget. Once moved it stops docking to position, and the panel expands from wherever it was dropped. |
appearance.launcherOptional | WidgetLauncherStyle | "avatar" | avatar idles the real character in the bubble; poster shows a still image; icon shows a generic mark. |
appearance.posterUrlOptional | string | No default | Shown before the character decodes, and as the entire launcher when launcher is set to poster. |
appearance.teaserOptional | WidgetTeaser | No default | A silent attract message beside the bubble, shaped { text, delayMs }. Audio before a user gesture is impossible, so attract behaviour is visual only. Default delay is 8000ms. |
appearance.mobileOptional | WidgetMobileMode | "sheet" | sheet takes over the viewport below 640px; corner keeps the desktop treatment at every width. |
appearance.zIndexOptional | number | 2147483000 | Just under the maximum, so a host page can still stack something above it if it must. |
AvatarWidgetProps extends WidgetConfig, so behaviour, context, and
limits are accepted by the type — but on the React path the component
ignores them. They belong to the hosted embed, where a deployment document
supplies them. In React, their equivalents go on the provider config instead:
behaviour.greeting becomes avatar.greeting,
behaviour.connectTone becomes connectTone, and behaviour.transcript becomes
transcript.
| Prop | Type | Default | Description |
|---|---|---|---|
titleOptional | string | No default | Heading shown at the top of the expanded panel. Omit it and the panel has no heading — most widgets do not need one. |
launcherLabelOptional | string | "Talk to the assistant" | Accessible label for the collapsed bubble, read by screen readers. |
autoStartOptional | boolean | true | Start the session on the same click that opens the panel. See the autoplay note below before turning this off. |
openOptional | boolean | No default | Controls the panel yourself. Omit it to let the widget own its own open state. |
onOpenChangeOptional | (open: boolean) => void | No default | Fires whenever the panel opens or closes, controlled or not. |
onStageChangeOptional | (stage: WidgetStage) => void | No default | Fires on every stage transition. See Stages below. |
onResizeOptional | (box: WidgetBox) => void | No default | Fires on every box change. The hosted embed loader resizes its iframe from this; in a React app you rarely need it. |
composerOptional | React.ReactNode | No default | Your own affordance rendered at the foot of the panel — a text input for tts mode, say. |
usePictureInPictureOptional | boolean | No default | Camera on the stage, avatar in the corner. Only honoured while expanded — the collapsed bubble is too small for two views. |
classNameOptional | string | No default | Applied to the widget root. |
styleOptional | React.CSSProperties | No default | Applied to the widget root. |
autoStart must ride the opening click. Browsers only unlock audio inside a
real user gesture. The widget starts the session in the same task as the launcher
click, which is what makes the avatar audible. If you turn autoStart off and
call start() later from an effect or a timer, the session will connect and the
avatar will stay mute.
onStageChange reports where the widget is. This is not session state — a widget
can be expanded with no session at all, and a call survives the panel being
collapsed.
| Stage | Meaning |
|---|---|
dormant | Collapsed to the bubble, no session. |
expanded | Panel open, no session running. |
connecting | Panel open, session starting. |
live | Call in progress. The launcher shows an elapsed clock. |
ended | The server sent a notice (limit, plan, or test-key expiry) and the call is closing. |
WidgetStage also declares "expanding", but the React widget never emits it —
it is reserved for the hosted embed. Do not write logic that waits for it.
When a call ends, an expanded panel folds itself back to the bubble. An open panel with a dead session is just a dead box.
useAvatarWidget() gives you the widget's presentation state and controls from
anywhere inside the provider. Session control stays on
useAvatar().
| Prop | Type | Default | Description |
|---|---|---|---|
isOpenOptional | boolean | No default | Panel expanded. Independent of the session. |
stageOptional | WidgetStage | No default | The current stage, the same value onStageChange receives. |
openOptional | () => void | No default | Expand the panel without starting a session. |
closeOptional | () => void | No default | Collapse the panel. Does not end a running call. |
toggleOptional | () => void | No default | Flip the panel open or closed. |
openAndStartOptional | () => void | No default | Expand and connect in one task. Call it from a real click handler, or the session comes up mute. |
To hold the open state in your own component instead, pass open and
onOpenChange and the widget becomes fully controlled.
The widget is a web-only component — it depends on position: fixed, viewport
geometry, and pointer dragging. @avatarfactory/react-native exports no widget.
On mobile, render Avatar inside your own screen
or modal and drive it with useAvatar().
The defaults are exported, so you can read them rather than copying the numbers: