Web Components

Customization

View as MarkdownOpen in Claude

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().

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

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:

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.

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

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

Per-element event payloads are listed in the Web Components reference.

Replacing a primitive

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

<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

NeedUse
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 componentsBrowser SDK directly

See Theming for the visual side and the Web Components reference for per-element attributes, slots, and events.