# `<!-- omitted: AvatarWidget -->`

The main component. Wraps a LiveKit session, mounts the avatar UI, routes agent commands, manages display state. Lives at `@livelayer/react`.

```tsx
import { AvatarWidget } from "@livelayer/react";
import "@livelayer/react/styles.css";

<!-- omitted: AvatarWidget -->
```

## Props by category

### Connection

<ParamsTable params={[
  { name: "agentId",         type: "string",  required: true,  description: "The published agent ID from app.livelayer.studio." },
  { name: "apiKey",          type: "string",  required: false, description: "Optional API key for cross-origin auth. Usually unnecessary; only set this if you self-host or use a custom session endpoint." },
  { name: "baseUrl",         type: "string",  required: false, description: "API base URL. Default: https://app.livelayer.studio. Override for self-hosted." },
  { name: "sessionEndpoint", type: "string",  required: false, description: "Custom endpoint that returns { url, token } for LiveKit session. Use this when your server mints tokens." },
  { name: "sessionBody",     type: "Record<string, unknown>", required: false, description: "Extra body fields included in the session POST. Useful for passing your own auth context (userId, plan, etc.)." },
]} />

### Display mode

The widget can be in three states: `hidden`, `minimized` (bubble), or `expanded` (full panel).

<ParamsTable params={[
  { name: "displayMode",          type: "\"hidden\" | \"minimized\" | \"expanded\"", required: false, description: "Controlled display state. Pass this to manage display from outside the component." },
  { name: "defaultDisplayMode",   type: "\"hidden\" | \"minimized\" | \"expanded\"", required: false, description: "Initial mode before the user toggles. Default: \"expanded\"." },
  { name: "onDisplayModeChange",  type: "(mode: DisplayMode) => void", required: false, description: "Callback fired when the user toggles between minimized/expanded/hidden." },
  { name: "experienceMode",       type: "\"WIDGET\" | \"EMBEDDED\"",     required: false, description: "Layout type. WIDGET = floating, position-aware. EMBEDDED = inline, fills parent." },
  { name: "position",             type: "\"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\" | \"custom\"", required: false, description: "Where the floating bubble docks. Use \"custom\" + className/style for full position control." },
  { name: "mobileBreakpoint",     type: "number | false", required: false, description: "Width threshold (px) for mobile layout. Default: 640. Pass false to disable mobile layout entirely." },
  { name: "persistKey",           type: "string", required: false, description: "localStorage key for saving display mode. Default: \"ll-widget\". Use distinct keys when rendering multiple widgets." },
  { name: "disablePersistence",   type: "boolean", required: false, description: "Skip localStorage persistence entirely." },
]} />

### Branding

```tsx
<!-- omitted: AvatarWidget -->
```

<!-- omitted: ParamsTable -->

### Capabilities

Restrict what page actions the agent is allowed to perform. Same idea as the [script-tag capabilities](/docs/develop/script-tag/capabilities), but type-safe and finer-grained — the React package supports form filling.

```tsx
import type { AgentCapability } from "@livelayer/react";

const capabilities: AgentCapability[] = [
  "navigate",
  "scroll",
  "click",
  "fill_forms",
  "submit_forms",
  "read_page",
];

<!-- omitted: AvatarWidget -->
```

<!-- omitted: ParamsTable -->

### Routing (SPA)

For Next.js App Router, React Router v6+, or any SPA where the URL changes without a full reload, pass `pathname` and wire `onNavigate`. Without these, the widget falls back to native anchor clicks.

<ParamsTable params={[
  { name: "pathname",          type: "string", required: false, description: "REQUIRED for Next.js App Router and React Router v6+. The current pathname. The widget uses this for showOn/hideOn evaluation and to invalidate caches between routes." },
  { name: "showOn",            type: "RoutePattern[]", required: false, description: "Patterns where the widget MAY render. RoutePattern = string (glob), RegExp, or function. Empty/omitted = render everywhere." },
  { name: "hideOn",            type: "RoutePattern[]", required: false, description: "Patterns where the widget will NOT render. Wins over showOn." },
  { name: "onNavigate",        type: "(href: string) => void", required: false, description: "Called when the agent emits a navigate command. Wire to router.push for SPA navigation; default falls back to anchor click + history.pushState." },
  { name: "onScrollToSelector", type: "(selector: string, behavior?: \"smooth\" | \"instant\") => void", required: false, description: "Called on scroll_to. Default scrolls the matched element into view." },
  { name: "onScrollPage",      type: "(direction: \"up\" | \"down\" | \"top\" | \"bottom\", behavior?: \"smooth\" | \"instant\") => void", required: false, description: "Called on scroll_page. Default scrolls the document." },
  { name: "onClick",           type: "(selector: string) => void", required: false, description: "Called on click commands. Default does document.querySelector(selector).click()." },
]} />

See [Routing](/docs/develop/npm/react/routing) for full examples.

### Page awareness

When the agent calls `request_page_context`, these props control what gets sent.

<ParamsTable params={[
  { name: "getPageContext",     type: "(extras?) => PageContext | Promise<PageContext>", required: false, description: "Custom DOM walker. Default extracts headings, links, form fields, visible text, marked regions. Override to feed the agent your structured app state." },
  { name: "pageContextExtras",  type: "Record<string, unknown>", required: false, description: "Free-form metadata always included in page context (e.g., currentUser, cartTotal)." },
  { name: "getRoutes",          type: "() => RouteEntryInput[] | Promise<RouteEntryInput[]>", required: false, description: "Override the route extraction. Default walks <a> elements; pass this to feed routes from your sitemap or API." },
]} />

See [Page awareness](/docs/develop/npm/react/page-awareness) for end-to-end examples.

### Lifecycle callbacks

```tsx
<AvatarWidget
  agentId="agt_abc123"
  onConnect={() => analytics.track("agent_connected")}
  onDisconnect={() => analytics.track("agent_disconnected")}
  onTranscript={(entries) => myStore.setTranscript(entries)}
  onAgentState={(state) => myStore.setAgentState(state)}
  onConnectionStateChange={(state) => console.log("conn:", state)}
/>
```

<ParamsTable params={[
  { name: "onConnect",                type: "() => void", required: false, description: "Session connected." },
  { name: "onDisconnect",             type: "() => void", required: false, description: "Session disconnected." },
  { name: "onTranscript",             type: "(entries: TranscriptEntry[]) => void", required: false, description: "Transcript updated. Fires for both partial and final entries." },
  { name: "onAgentState",             type: "(state: AgentState) => void", required: false, description: "Agent state changed: idle | listening | thinking | speaking." },
  { name: "onConnectionStateChange",  type: "(state: ConnectionState) => void", required: false, description: "Connection state changed: idle | connecting | connected | disconnected | error." },
  { name: "onAgentEvent",             type: "(e: AgentEventDetail) => void", required: false, description: "Observability sink. Fires for every agent event (navigation, scroll, fill, submit, custom). Useful for analytics or debugging." },
  { name: "onAgentCommand",           type: "(cmd: AgentCommand) => void", required: false, description: "Receives non-universal agent commands — anything you defined in the dashboard's tool config that isn't navigate/scroll/click/fill/submit. Use this for app-specific actions." },
]} />

### Sound effects

```tsx
<!-- omitted: AvatarWidget -->
```

<!-- omitted: ParamsTable -->

### Chrome / UI controls

<!-- omitted: ParamsTable -->

### Team switching

Render multiple agent personas and let the visitor switch between them — useful for multi-role demos (sales / support / engineering).

```tsx
<AvatarWidget
  agentId="agt_abc123"
  teamMembers={[
    { id: "sales",   name: "Sales", agentId: "agt_sales_xyz", avatarImageUrl: "/sales.png" },
    { id: "support", name: "Support", agentId: "agt_support_xyz", avatarImageUrl: "/support.png" },
  ]}
  currentTeamMemberId="sales"
  onTeamMemberChange={(member) => setCurrentMember(member.id)}
/>
```

<ParamsTable params={[
  { name: "teamMembers",          type: "TeamMember[]", required: false, description: "List of agents the visitor can switch between." },
  { name: "currentTeamMemberId",  type: "string", required: false, description: "Controlled current team member ID." },
  { name: "onTeamMemberChange",   type: "(member: TeamMember) => void", required: false, description: "Callback fired on switch." },
]} />

### Transformation overlay

Show a spinner overlay during avatar/voice transitions:

```tsx
<!-- omitted: AvatarWidget -->
```

<!-- omitted: ParamsTable -->

### Advanced: Controlled session

For cross-page persistence (one session that survives route changes) or shared rooms (multiple widgets, one session), bring your own session via `controlledSession`. See [Controlled session](/docs/develop/npm/react/controlled-session).

<!-- omitted: ParamsTable -->

### Styling

<!-- omitted: ParamsTable -->

## Imperative methods (ref handle)

The component uses `forwardRef` and exposes a small imperative API.

```tsx
import { useRef } from "react";
import { AvatarWidget, type AvatarWidgetHandle } from "@livelayer/react";

function App() {
  const ref = useRef<AvatarWidgetHandle>(null);

  const sendCustomEvent = async () => {
    await ref.current?.sendData({
      type: "user_action",
      action: "added_to_cart",
      productId: "sku_123",
    });
  };

  return <!-- omitted: AvatarWidget -->;
}
```

<ParamsTable params={[
  { name: "sendData", type: "(data: Record<string, unknown>) => Promise<void>", required: false, description: "Publish a JSON payload over the LiveKit data channel. The agent receives it as a data message. No-op if not connected." },
]} />

## Read next

- [Page awareness](/docs/develop/npm/react/page-awareness) — make the agent see your page
- [Routing](/docs/develop/npm/react/routing) — `showOn`, `hideOn`, `onNavigate` deep dive
- [Hooks](/docs/develop/npm/react/hooks) — build your own UI from primitives
- [TypeScript types](/docs/develop/npm/types) — every type reference
