Web (React) Guide

Build a browser softphone with the jambonz WebRTC SDK

This guide walks you through building a voice calling application in the browser using React.

Step 1: Create a React Project

npm create vite@latest my-jambonz-app -- --template react-ts
cd my-jambonz-app

Step 2: Install the SDK

npm install @jambonz/client-sdk-web

Step 3: Connect to the SBC

Create a component that connects to your jambonz SBC:

// src/App.tsx
import { useState } from 'react';
import { createJambonzClient, ClientState } from '@jambonz/client-sdk-web';
function App() {
const [client, setClient] = useState(null);
const [state, setState] = useState(ClientState.Disconnected);
const connect = async () => {
const c = createJambonzClient({
server: 'wss://sbc.example.com:8443',
username: 'your-username',
password: 'your-password',
});
c.on('stateChanged', (s) => setState(s));
c.on('error', (err) => console.error('Error:', err.message));
await c.connect();
setClient(c);
};
return (
<div>
<p>Status: {state}</p>
<button onClick={connect}>Connect</button>
</div>
);
}
export default App;

Run npm run dev and click Connect. You should see the status change to registered.

Step 4: Make an Outbound Call

Add a call button to your component:

const [call, setCall] = useState(null);
const [callState, setCallState] = useState(null);
const makeCall = () => {
const newCall = client.call('+15551234567');
newCall.on('stateChanged', (s) => setCallState(s));
newCall.on('accepted', () => console.log('Call connected'));
newCall.on('ended', (cause) => {
console.log('Call ended:', cause.reason);
setCall(null);
setCallState(null);
});
setCall(newCall);
};
// In your JSX:
<button onClick={makeCall} disabled={!client}>Call</button>
<button onClick={() => call?.hangup()} disabled={!call}>Hang Up</button>
<p>Call: {callState || 'none'}</p>

Step 5: Handle Incoming Calls

Listen for incoming calls and show an answer/decline UI:

const [incomingCall, setIncomingCall] = useState(null);
// Add this after creating the client:
c.on('incoming', (call) => {
setIncomingCall(call);
});
// In your JSX:
{incomingCall && (
<div>
<p>Incoming call from: {incomingCall.remoteIdentity}</p>
<button onClick={() => {
incomingCall.answer();
setCall(incomingCall);
setIncomingCall(null);
}}>Answer</button>
<button onClick={() => {
incomingCall.hangup();
setIncomingCall(null);
}}>Decline</button>
</div>
)}

Step 6: Add Call Controls

Add mute, hold, and DTMF:

const [isMuted, setIsMuted] = useState(false);
const [isHeld, setIsHeld] = useState(false);
// Listen for events:
newCall.on('mute', (muted) => setIsMuted(muted));
newCall.on('hold', (held) => setIsHeld(held));
// In your JSX:
<button onClick={() => call?.toggleMute()}>
{isMuted ? 'Unmute' : 'Mute'}
</button>
<button onClick={() => isHeld ? call?.unhold() : call?.hold()}>
{isHeld ? 'Resume' : 'Hold'}
</button>
<button onClick={() => call?.sendDTMF('1')}>Send 1</button>

Step 7: Using React Hooks (Alternative)

Instead of managing state manually, use the built-in hooks:

import { useJambonzClient, useCall } from '@jambonz/client-sdk-web';
function Phone() {
const client = useJambonzClient({
server: 'wss://sbc.example.com:8443',
username: 'your-username',
password: 'your-password',
});
const call = useCall(client.client);
return (
<div>
<p>Status: {client.state}</p>
{client.error && <p>Error: {client.error}</p>}
{!client.isRegistered && (
<button onClick={client.connect}>Connect</button>
)}
{client.isRegistered && !call.isActive && (
<button onClick={() => call.makeCall('+15551234567')}>Call</button>
)}
{call.isActive && (
<div>
<p>Call: {call.state}</p>
<button onClick={call.toggleMute}>
{call.isMuted ? 'Unmute' : 'Mute'}
</button>
<button onClick={call.toggleHold}>
{call.isHeld ? 'Resume' : 'Hold'}
</button>
<button onClick={call.hangup}>Hang Up</button>
</div>
)}
{call.incomingCaller && (
<div>
<p>Incoming: {call.incomingCaller}</p>
<button onClick={call.answerIncoming}>Answer</button>
<button onClick={call.declineIncoming}>Decline</button>
</div>
)}
</div>
);
}

Run the Full Example App

The repo includes a complete softphone example built with React + Vite + Tailwind CSS — with a polished dark theme UI, DTMF pad, incoming call handling, and console logs.

# 1. Clone the repo
git clone https://github.com/jambonz/webrtc-sdk.git
cd webrtc-sdk
# 2. Install and build the SDK
npm install
npm run build
# 3. Install and run the web example
cd examples/web
npm install
npm run dev

Open http://localhost:5173 in your browser.

What the example includes

The example app has clean separation between SDK logic and UI:

  • src/useJambonz.ts — all SDK interactions (connect, call, mute, hold, transfer, incoming calls). Read this file to learn the SDK.
  • src/App.tsx — wires SDK state to UI components
  • src/components/ — reusable UI: ConnectionForm, DialerView, ActiveCallView, IncomingCallView, DtmfPad, LogPanel

How to use it

  1. Enter your jambonz SBC WebSocket URL (e.g. wss://sbc.example.com:8443)
  2. Enter your SIP username and password
  3. Click Connect — status dot turns green when registered
  4. Enter a number or SIP URI and click Call
  5. Use the in-call controls: mute, hold, DTMF pad, hang up
  6. Incoming calls show an answer/decline screen
  7. Expand Console Logs at the bottom to see SDK events

Source: github.com/jambonz/webrtc-sdk/tree/main/examples/web