> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.jambonz.org/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.jambonz.org/_mcp/server.

# React Native Guide

This guide walks you through building a voice calling app for iOS and Android using React Native.

> **Note**
>
> The SDK works on both simulators/emulators and physical devices. A physical device is recommended for real call testing with audio.

## Step 1: Create a React Native Project

```bash
npx @react-native-community/cli init MyJambonzApp
cd MyJambonzApp
```

## Step 2: Install the SDK

```bash
npm install @jambonz/client-sdk-react-native react-native-webrtc
```

For iOS:

```bash
cd ios && pod install && cd ..
```

## Step 3: Android Permissions

Add to `android/app/src/main/AndroidManifest.xml` inside `<manifest>`:

```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
```

## Step 4: iOS Permissions

Add to `ios/MyJambonzApp/Info.plist`:

```xml
<key>NSMicrophoneUsageDescription</key>
<string>Required for voice calls</string>
```

## Step 5: Connect to the SBC

```tsx
// App.tsx
import React, { useState } from 'react';
import { View, Text, TextInput, Pressable } from 'react-native';
import {
  createJambonzClient,
  JambonzClient,
  ClientState,
} from '@jambonz/client-sdk-react-native';

function App() {
  const [client, setClient] = useState<JambonzClient | null>(null);
  const [state, setState] = useState<ClientState>(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.log('Error:', err.message));

    await c.connect();
    setClient(c);
  };

  return (
    <View style={{ padding: 40 }}>
      <Text>Status: {state}</Text>
      <Pressable onPress={connect}>
        <Text>Connect</Text>
      </Pressable>
    </View>
  );
}

export default App;
```

Run on your device:

```bash
# Android
npx react-native run-android

# iOS (physical device only)
npx react-native run-ios --device
```

## Step 6: Make an Outbound Call

```tsx
const [call, setCall] = useState(null);
const [callState, setCallState] = useState(null);

const makeCall = (target: string) => {
  if (!client) return;

  const newCall = client.call(target);
  newCall.on('stateChanged', (s) => setCallState(s));
  newCall.on('ended', () => {
    setCall(null);
    setCallState(null);
  });
  newCall.on('failed', () => {
    setCall(null);
    setCallState(null);
  });

  setCall(newCall);
};

// In your JSX:
<Pressable onPress={() => makeCall('+15551234567')}>
  <Text>Call</Text>
</Pressable>
<Pressable onPress={() => call?.hangup()}>
  <Text>Hang Up</Text>
</Pressable>
```

## Step 7: Handle Incoming Calls

Use React Native's `Alert` for a simple incoming call prompt:

```tsx
import { Alert } from 'react-native';

// After creating the client:
c.on('incoming', (incomingCall) => {
  Alert.alert(
    'Incoming Call',
    `From: ${incomingCall.remoteIdentity}`,
    [
      {
        text: 'Decline',
        style: 'destructive',
        onPress: () => incomingCall.hangup(),
      },
      {
        text: 'Answer',
        onPress: () => {
          incomingCall.answer();
          setCall(incomingCall);
          // Bind call events...
        },
      },
    ],
  );
});
```

## Step 8: Add Call Controls

```tsx
const [isMuted, setIsMuted] = useState(false);
const [isHeld, setIsHeld] = useState(false);

// Bind events on the call:
newCall.on('mute', (muted) => setIsMuted(muted));
newCall.on('hold', (held) => setIsHeld(held));

// In your JSX:
<Pressable onPress={() => call?.toggleMute()}>
  <Text>{isMuted ? 'Unmute' : 'Mute'}</Text>
</Pressable>
<Pressable onPress={() => isHeld ? call?.unhold() : call?.hold()}>
  <Text>{isHeld ? 'Resume' : 'Hold'}</Text>
</Pressable>
```

## Step 9: Call Different Targets

```tsx
// Call another registered user
client.callUser('alice');

// Take a call from a queue
client.callQueue('support');

// Join a conference room
client.callConference('standup-meeting');

// Call a jambonz application
client.callApplication('your-app-sid');
```

## Step 10: Using React Hooks (Alternative)

```tsx
import { useJambonzClient, useCall } from '@jambonz/client-sdk-react-native';

function Phone() {
  const client = useJambonzClient({
    server: 'wss://sbc.example.com:8443',
    username: 'your-username',
    password: 'your-password',
  });

  const call = useCall(client.client);

  return (
    <View>
      <Text>Status: {client.state}</Text>

      {!client.isRegistered && (
        <Pressable onPress={client.connect}>
          <Text>Connect</Text>
        </Pressable>
      )}

      {client.isRegistered && !call.isActive && (
        <Pressable onPress={() => call.makeCall('+15551234567')}>
          <Text>Call</Text>
        </Pressable>
      )}

      {call.isActive && (
        <>
          <Text>Call: {call.state}</Text>
          <Pressable onPress={call.toggleMute}>
            <Text>{call.isMuted ? 'Unmute' : 'Mute'}</Text>
          </Pressable>
          <Pressable onPress={call.hangup}>
            <Text>Hang Up</Text>
          </Pressable>
        </>
      )}
    </View>
  );
}
```

## Android Notes

* **JDK 17+** required — install via `brew install --cask zulu@17`
* **USB debugging** must be enabled for physical devices
* Works on both emulator and physical device

## iOS Notes

* **Xcode 15+** required
* You must configure **code signing** in Xcode (Signing & Capabilities tab)
* Add **microphone permission** in Info.plist
* Works on both simulator and physical device (physical device recommended for real calls)

## Run the Full Example App

The repo includes a complete softphone example with a dark theme UI, DTMF dial pad, incoming call handling, mute/hold/hangup controls.

```bash
# 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 the React Native example
cd examples/react-native
npm install

# 4. Generate the native projects
npx @react-native-community/cli init JambonzExample --directory /tmp/JambonzExample --skip-install
cp -r /tmp/JambonzExample/android ./android
cp -r /tmp/JambonzExample/ios ./ios
rm -rf /tmp/JambonzExample
```

After generating, add the required permissions:

**Android** — add to `android/app/src/main/AndroidManifest.xml` inside `<manifest>`:

```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
```

**iOS** — add to `ios/JambonzExample/Info.plist` before `</dict>`:

```xml
<key>NSMicrophoneUsageDescription</key>
<string>Required for voice calls</string>
```

#### Run on Android

```bash
# Start Metro bundler
npx react-native start

# In another terminal — run on device or emulator
npx react-native run-android
```

#### Run on iOS

```bash
# Install CocoaPods
cd ios && pod install && cd ..

# Open Xcode to configure signing
open ios/JambonzExample.xcworkspace
# → Select your Team in Signing & Capabilities
# → Change Bundle Identifier to something unique

# Start Metro bundler
npx react-native start

# In another terminal — run on device or simulator
npx react-native run-ios
# Or for a specific device:
npx react-native run-ios --device
```

#### 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
* **`src/theme.ts`** — shared color palette

#### 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. Tap **Connect** — status dot turns green when registered
4. Enter a number or SIP target and tap **Call**
5. Use the in-call controls: mute, hold, DTMF pad, hang up
6. Incoming calls show an answer/decline prompt

Source: [github.com/jambonz/webrtc-sdk/tree/main/examples/react-native](https://github.com/jambonz/webrtc-sdk/tree/main/examples/react-native)