@upscopeio/react-native-sdk
v2026.7.3
Published
React Native SDK for Upscope cobrowsing
Downloads
1,136
Readme
@upscopeio/react-native-sdk
React Native SDK for Upscope cobrowsing — screen sharing, agent annotations, and remote control for iOS and Android.
Requirements
- React Native 0.76+
- iOS 14+
- Android API 26+ (Android 8.0 Oreo)
- New Architecture (Fabric + TurboModules) required
Installation
npm install @upscopeio/react-native-sdkiOS
Run pod install — the native UpscopeSDK is linked automatically via this SDK's podspec dependency, so its version always matches the published package. No extra steps.
cd ios && pod installBy default the SDK shares only your app's UI. To let agents view the visitor's entire device, see Full device sharing (iOS).
Android
No extra steps — the native module and Upscope Android SDK are linked automatically via autolinking.
Quick Start
Initialize in your app entry point (before rendering any components that use Upscope hooks):
import Upscope from '@upscopeio/react-native-sdk';
// App.tsx — call once, as early as possible
Upscope.initialize({
apiKey: 'your-api-key',
autoConnect: true,
requireAuthorizationForSession: true,
});
// Identify the visitor
Upscope.updateConnection({
uniqueId: 'user-123',
callName: 'Jane Doe',
tags: ['support'],
identities: ['[email protected]'],
integrationIds: ['intercom:user123'],
metadata: { plan: 'premium' },
});If autoConnect is false, call Upscope.connect() manually after initialization.
Hooks
All hooks are reactive — they re-render the component when the underlying state changes.
useConnectionState
Returns the current WebSocket connection state.
import { useConnectionState } from '@upscopeio/react-native-sdk';
function StatusBadge() {
const state = useConnectionState();
// state: 'inactive' | 'connecting' | 'connected' | 'reconnecting' | 'error'
return <Text>Connection: {state}</Text>;
}useSessionState
Returns the current co-browsing session state.
import { useSessionState } from '@upscopeio/react-native-sdk';
function SessionBanner() {
const session = useSessionState();
// session: 'inactive' | 'pendingRequest' | 'active' | 'paused' | 'ended'
if (session !== 'active') return null;
return <Text>Session in progress</Text>;
}useShortId
Returns the SDK short ID (used to look up the visitor in the Upscope dashboard), or null until assigned.
import { useShortId } from '@upscopeio/react-native-sdk';
function VisitorId() {
const shortId = useShortId();
return <Text>Short ID: {shortId ?? 'pending...'}</Text>;
}useLookupCode
Returns the current lookup code shown to the visitor for phone-based agent lookup, or null until emitted.
import { useLookupCode } from '@upscopeio/react-native-sdk';
function LookupCode() {
const code = useLookupCode();
return <Text>Your code: {code ?? '—'}</Text>;
}useRemoteControlState
Returns whether an agent currently has remote control of the device.
import { useRemoteControlState } from '@upscopeio/react-native-sdk';
function ControlBadge() {
const state = useRemoteControlState();
// state: 'inactive' | 'pendingRequest' | 'active'
return <Text>Remote control: {state}</Text>;
}useFullDeviceSharingState
Returns whether full-device (broadcast) sharing is currently running.
import { useFullDeviceSharingState } from '@upscopeio/react-native-sdk';
function SharingBadge() {
const state = useFullDeviceSharingState();
// state: 'inactive' | 'pendingRequest' | 'active'
return <Text>Full-device sharing: {state}</Text>;
}useUpscope
Returns a stable object of imperative action methods — safe to destructure and pass as deps.
import { useUpscope } from '@upscopeio/react-native-sdk';
function Controls() {
const { connect, disconnect, stopSession, sendCustomMessage } = useUpscope();
return (
<>
<Button title="Connect" onPress={connect} />
<Button title="Disconnect" onPress={disconnect} />
<Button title="End Session" onPress={stopSession} />
</>
);
}Events
Subscribe to native events imperatively via Upscope.addListener. Always call .remove() on cleanup.
import Upscope from '@upscopeio/react-native-sdk';
import { useEffect } from 'react';
useEffect(() => {
const sub = Upscope.addListener('sessionStarted', ({ agentName }) => {
console.log(`Session started with ${agentName}`);
});
return () => sub.remove();
}, []);Event names
| Event name | Payload type | Description |
| ------------------------- | ----------------------------- | ---------------------------------------- |
| connectionStateChanged | ConnectionStateChangedEvent | Connection state transitioned |
| sessionStateChanged | SessionStateChangedEvent | Session state transitioned (current-value)|
| sessionStarted | SessionStartedEvent | Agent joined and session is active |
| sessionEnded | SessionEndedEvent | Session ended (includes reason) |
| customMessageReceived | CustomMessageEvent | Arbitrary string from a viewer |
| error | UpscopeError | Non-fatal SDK error |
| viewerJoined | Viewer | New viewer connected |
| viewerLeft | ViewerLeftEvent | Viewer disconnected |
| viewerCountChanged | ViewerCountChangedEvent | Total viewer count changed |
| shortIdChanged | ShortIdChangedEvent | Short ID assigned or rotated |
| lookupCodeChanged | LookupCodeChangedEvent | Lookup code issued or changed |
| remoteControlStateChanged | RemoteControlStateChangedEvent | Agent gained or lost remote control |
| fullDeviceSharingStateChanged | FullDeviceSharingStateChangedEvent | Full-device sharing started or stopped |
| fullDeviceRequest | FullDeviceRequestEvent | Agent requested full-device sharing — includes the requesting agentName; answer with respondToFullDeviceRequest |
| controlRequest | ControlRequestEvent | Agent requested remote control — includes the requesting agentName; answer with respondToControlRequest |
View Masking
Wrap any sensitive content in UpscopeMasked to hide it from the agent's view during a session. The masked region appears as a solid block in the screen share.
import { UpscopeMasked } from '@upscopeio/react-native-sdk';
function PaymentForm() {
return (
<View>
<Text>Card number</Text>
<UpscopeMasked>
<TextInput secureTextEntry placeholder="•••• •••• •••• ••••" />
</UpscopeMasked>
</View>
);
}UpscopeMasked accepts all standard ViewProps in addition to children.
Configuration
Pass a UpscopeConfig object to Upscope.initialize():
interface UpscopeConfig {
apiKey: string; // Required. Your Upscope API key.
requireAuthorizationForSession?: boolean; // Show a prompt before allowing a session. Default: true.
autoConnect?: boolean; // Connect on initialize(). Default: false.
region?: string; // Override the server region.
showTerminateButton?: boolean; // Show an end-session button during a session.
showUpscopeLink?: boolean; // Show the "Powered by Upscope" link. Hiding it requires the whitelabel feature on the account.
authorizationPromptTitle?: string; // Custom title for the session request dialog.
authorizationPromptMessage?: string; // Custom message for the session request dialog.
endOfSessionMessage?: string; // Message shown when a session ends.
stopSessionText?: LocalizedString; // Label for the terminate-session button.
allowRemoteClick?: boolean; // Allow agent to send tap events. Default: true.
allowRemoteScroll?: boolean; // Allow agent to send scroll events. Default: true.
requireControlRequest?: boolean; // Require visitor approval before remote control starts.
broadcastAppGroupId?: string; // iOS only. App Group shared with the Broadcast Upload Extension (enables full-device sharing).
broadcastExtensionBundleId?: string; // iOS only. Bundle id of the Broadcast Upload Extension.
}
// Translation fields accept either a plain string or a language-keyed map.
// Keys are BCP-47 language tags; the native SDK picks the best match for the
// device's preferred languages.
type LocalizedString = string | { [language: string]: string };Example:
Upscope.initialize({
apiKey: "…",
stopSessionText: { en: "Stop", it: "Ferma", "pt-PT": "Parar" },
});Full device sharing (iOS)
Full device sharing lets the agent see the entire device screen (other apps, home screen) via ReplayKit. It requires a Broadcast Upload Extension target in your app — npm packages can't add app extension targets, so this is a one-time manual setup (~10 minutes). Without it, the agent's "Full app sharing" button is hidden.
Android needs no setup: it uses MediaProjection, and the required foreground-service permissions are merged automatically from the SDK's manifest.
1. Add the extension target
In Xcode: File → New → Target → Broadcast Upload Extension. Name it (e.g.
YourAppBroadcast), uncheck "Include UI Extension".
2. Replace the generated SampleHandler
import UpscopeSDK
class SampleHandler: UpscopeSampleHandler {}3. Add an App Group to BOTH targets
In Signing & Capabilities for your app target and the extension target,
add the App Groups capability with the same group, e.g.
group.com.yourcompany.yourapp.
4. Declare the group in the extension's Info.plist
<key>UpscopeAppGroupId</key>
<string>group.com.yourcompany.yourapp</string>Also make sure NSExtension > RPBroadcastProcessMode is 2 (sample-buffer
mode); some Xcode template versions omit it. See the example app's
Info.plist.
5. Add the extension pod
# Podfile (top level, alongside your app target)
target 'YourAppBroadcast' do
pod 'UpscopeSDK/BroadcastExtension'
endRun pod install.
6. Pass the keys at initialization
Upscope.initialize({
apiKey: 'your-api-key',
broadcastAppGroupId: 'group.com.yourcompany.yourapp',
broadcastExtensionBundleId: 'com.yourcompany.yourapp.YourAppBroadcast',
});broadcastAppGroupId enables the feature (the agent's button appears);
broadcastExtensionBundleId preselects your extension in the iOS picker.
Notes
- The App Group ID must match in three places: both targets' entitlements,
the extension's
Info.plist(UpscopeAppGroupId), andbroadcastAppGroupId. - Test on a physical device — ReplayKit broadcasts are unreliable on the simulator.
- Full-device capture cannot mask views. Set
disableFullScreenWhenMasked: trueto automatically end full-device sharing whenever masked content is on screen. - Expo managed workflow is not supported (extension targets require a config plugin; not currently provided).
- A complete working setup is in
example/ios(UpscopeExampleBroadcasttarget).
API Reference
Initialization
| Method | Description |
| ----------------------------------- | --------------------------------------------------- |
| initialize(config) | Initialize the SDK. Call once before everything else. |
| connect() | Open the WebSocket connection. |
| disconnect() | Close the WebSocket connection. |
| reset(reconnect?) | Reset connection state. Reconnects by default. |
Visitor Identity
| Method | Description |
| ----------------------------------- | --------------------------------------------------- |
| updateConnection(params) | Set/update uniqueId, callName, tags, identities, integrationIds, metadata. |
Session
| Method | Description |
| ----------------------------------- | --------------------------------------------------- |
| stopSession() | Terminate the active session. |
| requestAgent() | Request that an agent join. |
| cancelAgentRequest() | Cancel a pending agent-join request. |
| stopRemoteControl() | Revoke the agent's remote control, keep the session.|
| stopFullDeviceSharing() | Stop full-device sharing, revert to in-app, keep the session. |
| respondToFullDeviceRequest(id, accept) | Answer a fullDeviceRequest event. |
| respondToControlRequest(id, accept) | Answer a controlRequest event. |
Data
| Method | Returns | Description |
| ----------------------------------- | -------------------- | ------------------------------------- |
| getShortId() | Promise<string \| null> | Resolve the current short ID. |
| getWatchLink() | Promise<string \| null> | Resolve the session watch link. |
| getLookupCode() | void | Trigger a lookupCodeChanged event. |
Messaging
| Method | Description |
| ----------------------------------- | --------------------------------------------------- |
| sendCustomMessage(message) | Send a string to all connected viewers. |
Events
| Method | Description |
| ----------------------------------- | --------------------------------------------------- |
| addListener(eventName, callback) | Subscribe to an event. Returns { remove() }. |
Example App
A full-featured example app is included in example/. It demonstrates initialization, connection, visitor identification, view masking, and event logging.
Running the example
# Install dependencies
cd example
npm install
# iOS
cd ios && pod install && cd ..
npm run ios
# Android
npm run androidThe app prompts for an API key on launch, then shows all SDK features in a scrollable demo screen.
Troubleshooting
"SDK not initialized" — Upscope.initialize() must be called before connect() or any hook mounts.
Session prompt never appears — Ensure requireAuthorizationForSession: true (default). On Android, the app must be in the foreground.
Masked views visible to agent — UpscopeMasked must be a direct native view wrapper. Confirm autolinking ran and pod install completed on iOS.
TypeScript errors on event payloads — Import the specific payload type from @upscopeio/react-native-sdk, e.g. import type { SessionStartedEvent } from '@upscopeio/react-native-sdk'.
License
MIT
