Migrate from v3
This guide walks through moving an existing v3 (@signalwire/js@3.x) integration to v4. v3 was built around RoomSession and event emitters for video conferencing. v4 unifies calling and conferencing under a single Call API, replaces event emitters with RxJS observables, and introduces a credential-provider auth model with automatic token refresh.
At a glance
Feature compatibility
v4 covers the bulk of v3, but some features are still in progress. Check this table before migrating.
If your application depends on recording, streaming, playback, room locking, metadata, or transfer, wait for these features to land before migrating.
Migration checklist
- Update the package and import paths
- Replace
await SignalWire({ token })withnew SignalWire(credentialProvider) - Remove
rootElementfromdial()and attach media streams manually (or use web components) - Drop
node_id/userVariables/await call.start()— handled by v4 internally - Convert
RoomSessionmethods toCall/call.selfequivalents - Replace
roomObj.on('event', ...)withcall.eventName$.subscribe(...) - Update
invite.accept/invite.rejecttocall.answer()/call.reject() - Drop
client.online()/client.offline()— registration is automatic (useclient.unregister()to go offline) - Move screen share from the room to
call.self - Swap
WebRTC.getCameras()etc. forclient.videoInputDevices$ - Pass full
MediaDeviceInfoobjects (not baredeviceId) to device selectors - Replace
client.address.getAddresses()withclient.directory.addresses$ - Replace
client.conversationmessaging withcallAddress.sendText()/textMessages$ - Add explicit cleanup:
call.hangup(),client.disconnect(),client.destroy()
Installation
The package name is unchanged. Upgrade to the v4 major release:
For the browser build:
v4 ships as an ES module. If you bundled v3 as a CDN global, switch to module imports:
Authentication
v3 accepted a room token directly. v4 introduces a CredentialProvider that owns the token lifecycle — including scheduled refresh before expiry. Use Subscriber Access Tokens (SAT) for authenticated users and Embed Tokens for guest access. See Authentication for the full reference.
The SDK ships with StaticCredentialProvider for pre-obtained tokens (build-time SAT, server-rendered pages). For long-running apps, implement a custom provider that fetches and refreshes a SAT from your backend.
Client Bound SAT (DPoP)
When the SDK passes an AuthenticateContext with a DPoP key fingerprint, forward it to your token endpoint to request a Client Bound SAT with automatic refresh:
Client initialization
v3 was an async factory. v4 is a synchronous constructor; connection happens automatically when you subscribe to the first observable.
Before (v3):
After (v4):
By default, the client connects and registers automatically on construction. Pass skipConnection: true or skipRegister: true if you want to drive that lifecycle yourself.
Connection state
v3 had no separate connection observable — the factory was the connect call. v4 exposes connection state reactively:
Outbound calls
v3 took an options object with to, rootElement, optional nodeId for routing, and userVariables, then required await call.start(). v4 takes the destination as the first argument, handles steering internally, and does not auto-attach media — you wire streams up yourself.
Before (v3):
After (v4):
Full call lifecycle
Inbound calls
v3 used client.online({ incomingCallHandlers }) with callbacks and an explicit offline(). v4 registers the user automatically on construction and exposes incoming calls as an observable — no online/offline toggle. To go offline for inbound calls, call client.unregister() (and client.register() to come back online). answer() and reject() are synchronous in v4 — no await needed.
Before (v3):
After (v4):
RoomSession → Call
v3 distinguished between CallFabricRoomSession and RoomSession. v4 collapses both into a single Call, with self-participant controls moved off the room object onto call.self.
Self participant
call.self is a full participant object with reactive state.
Participants
Event emitters are gone — participants are an observable list. Each participant also exposes individual observables for granular updates.
Before (v3):
After (v4):
Screen sharing
Screen sharing moves from the room to call.self.
Layouts
Recording and streaming
Recording and streaming APIs are not yet implemented in v4. The observables exist for monitoring server-initiated state, but startRecording() and startStreaming() will throw. Drive these from SWML or the server-side REST API in the meantime.
Device management
The standalone WebRTC namespace and roomObj.updateCamera()-style methods are removed. Devices live on the client as reactive lists that auto-update when devices are plugged in or removed.
Heads up: v4 device selectors take the full
MediaDeviceInfoobject, not just adeviceIdstring.
Before (v3):
After (v4):
User info
Subscriber is renamed to User.
Before (v3):
After (v4):
Directory
v3’s paginated client.address.getAddresses() is replaced by a reactive directory that accumulates entries on loadMore().
Before (v3):
After (v4):
You can dial an address directly:
Messaging
v3’s client.conversation API is replaced by per-address messaging on the call.
Before (v3):
After (v4):
Messages are scoped to the current call’s address — there is no global conversation client in v4.
Removed namespaces
The standalone Chat, PubSub, and WebRTC clients from v3 are removed. Device APIs move onto the client (see Device management). Chat/PubSub equivalents are not part of the v4 browser SDK.
Event-to-observable reference
When using RxJS operators like
filter,map, orpipe, import them fromrxjs:See the RxJS primer for a quick orientation.
API quick reference
Cleanup
v4 requires explicit cleanup. End calls with hangup(), then disconnect and destroy the client to release all subscriptions.
Web components
v4 ships @signalwire/web-components, composable around the new reactive Call API. <sw-call-media> is the root container — nest media, controls, and status components inside, then assign the call.
<sw-participants> renders participant overlays driven by the same context.
Common migration issues
- No video displays. v4 does not auto-attach to the DOM. Subscribe to
remoteStream$(andlocalStream$) and assign the stream to a<video>’ssrcObject— or use<sw-call-media>/<sw-self-media>. call.selfis null.selfis populated only after joining. Usecall.self$for reactive access, or optional chaining (call.self?.audioMuted) for sync reads.- Events seem to be missing. Subscribe to observables before the events fire — and avoid unsubscribing prematurely.
participants$re-emits the full list on any change, so wire it up early in your component lifecycle. startRecording()throws. Recording is not yet implemented in v4. Trigger recording server-side via SWML or the REST API; usecall.recording$to reflect state in the UI.- Device selection has no effect. v4 expects a full
MediaDeviceInfoobject, not a baredeviceIdstring. - Token expired errors after a while. v3’s
client.updateToken()is gone. Implementrefresh()on your credential provider and return{ token, expiry_at }— the SDK will refresh on schedule.