> For a complete index of all SignalWire documentation pages, fetch https://signalwire.com/docs/llms.txt

# Customization

When [`<sw-call-widget>`] doesn't fit the layout, drop down to the
primitives. The widget itself is built from the same elements,
wired together through reactive contexts.

Three patterns, in increasing order of customization:

1. **Slots** — keep `<sw-call-widget>`, replace one region (trigger
   or background).
2. **Events & methods** — attribute / event / method API; the widget
   still owns the lifecycle.
3. **Primitives** — drop the widget and compose
   `<sw-call-provider>`, `<sw-call-media>`, `<sw-call-controls>`,
   etc. directly.

## Slots

[`<sw-call-widget>`] exposes a `background` slot for the full-bleed
background and a default slot for the idle-state trigger. Anything
in the default slot becomes the click target; clicking calls
`widget.dial()`.

```html
<sw-call-widget modal token="c2c_…" destination="/public/sales">
  <sw-ui-background slot="background" default></sw-ui-background>

  <button class="cta">
    <svg><!-- your icon --></svg>
    Talk to sales
  </button>
</sw-call-widget>
```

Once dialing starts the widget swaps into call mode (inline or
modal, per the `modal` attribute).

## Programmatic control

[`<sw-call-widget>`] exposes `dial()` and `hangup()` for driving
dialing from JS instead of the trigger slot:

```js
const widget = document.querySelector("sw-call-widget");

document.querySelector("#dial-btn").addEventListener("click", async () => {
  try {
    await widget.dial();
  } catch (err) {
    console.error("dial failed", err);
  }
});

document.querySelector("#hangup-btn").addEventListener("click", () => {
  widget.hangup();
});
```

The widget bubbles `sw-dial`, `sw-call-ended`, and forwarded agent
events:

```js
widget.addEventListener("sw-call-ended", (e) => {
  analytics.track("call_ended", { status: e.detail.status });
});
```

See [`<sw-call-widget>`] for the full event surface.

## Pass-through attributes

Widget attributes toggle sub-features:

* `transcription` — enables the AI transcript drawer.
* `allow-incoming-calls` — listens for inbound calls on the same token.
* `audio-only` — skips the camera.
* `user-variables` — JSON string forwarded into the Verto invite for
  the receiving side to read.

See [`<sw-call-widget>`] for the full attribute list.

## Building from primitives

For full layout control, compose the primitives directly.
`<sw-call-provider>` is the only required wrapper — it owns the
reactive contexts that the SDK-aware components subscribe to.

```html
<sw-call-provider id="provider">
  <div class="my-grid">
    <sw-call-media></sw-call-media>
    <sw-self-media></sw-self-media>
    <sw-call-status></sw-call-status>
    <sw-call-controls show-screen-share show-fullscreen></sw-call-controls>
    <sw-device-selector></sw-device-selector>
  </div>
</sw-call-provider>

<script type="module">
  import { SignalWire, StaticCredentialProvider } from "@signalwire/js";
  import "@signalwire/web-components";

  const client = new SignalWire(
    new StaticCredentialProvider({ token: "YOUR_SAT" })
  );

  const call = await client.dial("/private/sales", { audio: true, video: true });

  const provider = document.getElementById("provider");
  provider.call = call;
  provider.deviceController = client.deviceController;
</script>
```

For the embed bundle, replace the imports with
`const { SignalWire, StaticCredentialProvider } = SignalWireUI;` and
drop `<script type="module">`.

### Provider contexts

`<sw-call-provider>` mounts reactive contexts for call state,
devices, transcript, and forwarded user events. Set `.call` and
`.deviceController`; every nested SDK-aware element subscribes
automatically. State changes (participant joins, device changes,
transcript lines) push through the contexts to whichever components
are listening.

### Component events

Each primitive bubbles its own composed events:
`<sw-call-controls>` emits `sw-mute-audio` / `sw-mute-video` /
`sw-hangup`; `<sw-call-dialpad>` emits `sw-digit-press`;
`<sw-device-selector>` emits `sw-device-change`. Events bubble
across shadow DOM:

```js
provider.addEventListener("sw-hangup", () => {
  // user clicked hangup — clean up app state
});
```

Per-element event payloads are listed in the
[Web Components reference](/docs/browser-sdk/v4/reference/web-component).

### Replacing a primitive

Because components subscribe to context independently, a custom
element that dispatches the same composed events is a drop-in
replacement.

```html
<sw-call-provider .call="${call}">
  <sw-call-media></sw-call-media>
  <my-fancy-dialpad></my-fancy-dialpad>      <!-- emits sw-digit-press -->
  <sw-call-controls></sw-call-controls>
</sw-call-provider>
```

SDK-aware components don't talk to each other directly — they all
go through the call object — so any element emitting the right
events fits.

## Choosing the right level

| Need                                        | Use                                                          |
| ------------------------------------------- | ------------------------------------------------------------ |
| One call button on a page                   | [`<sw-click-to-call>`]                                       |
| Inline or modal call UI with default layout | [`<sw-call-widget>`]                                         |
| Same with a custom trigger or background    | [`<sw-call-widget>`] + slots                                 |
| Custom layout, keep SDK-aware primitives    | `<sw-call-provider>` + primitives                            |
| No web components                           | [Browser SDK](/docs/browser-sdk/v4/guides/overview) directly |

See [Theming](/docs/browser-sdk/v4/guides/web-components-theming)
for the visual side and the
[Web Components reference](/docs/browser-sdk/v4/reference/web-component)
for per-element attributes, slots, and events.

[`<sw-call-widget>`]: /docs/browser-sdk/v4/reference/web-component/sw-call-widget

[`<sw-click-to-call>`]: /docs/browser-sdk/v4/reference/web-component/sw-click-to-call