# `<livelayer-widget>` web component

The custom HTML element registered by `@livelayer/sdk`. The same element used in the [Script tag](/docs/develop/script-tag/overview) path — but here you're importing it as part of your bundle instead of via CDN.

## Registration

The element auto-registers when you import the SDK module:

```ts title="bootstrap.ts"
import "@livelayer/sdk";
```

After this import, `<livelayer-widget>` is available globally as a custom element. Use it anywhere in your HTML / JSX / template.

## Attributes

Same set as the Script-tag path. See the [Attributes reference](/docs/develop/script-tag/attributes) for the full list. Most-common:

```html
<livelayer-widget
  agent-id="agt_abc123"
  mode="widget"
  position="bottom-right"
  capabilities="navigate,scroll,click">
</livelayer-widget>
```

## Why use the web component instead of the React package?

If you're in a React app, prefer `<AvatarWidget>` from `@livelayer/react` — you get React-native lifecycle, hooks integration, page awareness, controlled session, and form filling. The web component does NOT support form filling.

Use `<livelayer-widget>` directly when:

- You're in Vue, Svelte, Solid, Lit, Alpine, or vanilla JS.
- You're in a non-React framework that doesn't have an SDK wrapper yet.
- You want shadow-DOM isolation (the widget's styles can't be touched by your app's CSS).
- You want the smallest possible integration footprint.

## Manipulating from JavaScript

The element behaves like any other DOM node. Setting attributes triggers the widget's `attributeChangedCallback`:

```ts title="programmatic.ts"
import "@livelayer/sdk";

const widget = document.createElement("livelayer-widget");
widget.setAttribute("agent-id", "agt_abc123");
widget.setAttribute("mode", "widget");
widget.setAttribute("capabilities", "navigate,scroll");
document.body.appendChild(widget);

// Later: change agent
widget.setAttribute("agent-id", "agt_xyz789");

// Listen to lifecycle events
widget.addEventListener("livelayer:opened", () => {
  console.log("Visitor opened the widget");
});

widget.addEventListener("livelayer:conversation:started", (e) => {
  console.log("Conversation:", e.detail);
});

// Remove from page (cleans up session)
widget.remove();
```

Lifecycle methods exposed on the element:

- `connectedCallback()` — fires when the element is added to the DOM
- `attributeChangedCallback(name, oldValue, newValue)` — fires on attribute changes
- `disconnectedCallback()` — fires when the element is removed; aborts in-flight fetches and destroys the session

## Shadow DOM

The widget renders into its own shadow root. CSS from your app cannot leak into it; CSS from inside it cannot leak out. The agent inside cannot read your page's text or fill your forms (this is a deliberate boundary).

If you need page awareness or form filling, use the [React package](/docs/develop/npm/react/page-awareness).

## Capabilities

`capabilities="navigate,scroll,click"` restricts what the agent can do on your host page. See [Script tag → Capabilities](/docs/develop/script-tag/capabilities).

The web component **cannot** do form filling — that's a React-only feature because it requires React-state coherence.

## Multiple widgets, one page

You can have multiple `<livelayer-widget>` elements on the same page; each runs an independent session. They don't share state.

For shared state (one session, multiple surfaces), drop the web component and use [`LiveKitSession`](/docs/develop/npm/sdk/livekit-session) directly.

## Dimensions and styling

- **`mode="widget"`** — element is `display: contents` (takes no layout space). Renders a fixed-position bubble inside the shadow root.
- **`mode="embedded"`** — element fills 100% of its parent's width and height. Wrap it to control dimensions.
- **`mode="fullscreen"`** — element renders fixed at 100vw × 100vh.

## Events

The widget dispatches CustomEvents on itself, so you can listen at the element level:

```ts
widget.addEventListener("livelayer:loaded", () => { /* ... */ });
widget.addEventListener("livelayer:opened", () => { /* ... */ });
widget.addEventListener("livelayer:conversation:started", (e) => { /* e.detail.id */ });
widget.addEventListener("livelayer:conversation:ended", (e) => { /* e.detail.durationSeconds */ });
widget.addEventListener("livelayer:agent:state", (e) => { /* e.detail.state */ });
widget.addEventListener("livelayer:transcript", (e) => { /* e.detail.entries */ });
widget.addEventListener("livelayer:error", (e) => { /* e.detail.code, e.detail.message */ });
```

Same event catalog as the script-tag path — see [Events](/docs/develop/script-tag/events).

## Read next

- [`LiveKitSession`](/docs/develop/npm/sdk/livekit-session) — programmatic session without the widget UI
- [Script-tag attributes](/docs/develop/script-tag/attributes) — full attribute reference
- [Capabilities](/docs/develop/script-tag/capabilities) — restrict agent page actions
