npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@levelchat/react-native

v0.2.0

Published

LevelChat React Native SDK — WebRTC rooms, simulcast/SVC, real-time chat and recording for iOS and Android. Mirrors the @levelchat/web public API.

Readme

@levelchat/react-native

LevelChat React Native SDK — production-grade WebRTC rooms for iOS and Android. Mirrors the public API of @levelchat/web, so a multi-platform app writes one integration layer and switches at the import.

import { LevelChat } from '@levelchat/react-native';
import { useRoomState, useParticipantViews } from '@levelchat/react-native/hooks';

Features

  • 🎥 Real WebRTC publish + subscribe via react-native-webrtc
  • 🧱 Mediasoup transport — exactly the same wire protocol as the Web SDK
  • 📺 Simulcast (q/h/f layers) and SVC (L3T3_KEY) on supported codecs
  • 🎙️ AVAudioSession (iOS) + AudioManager (Android) configured for video calls
  • 🔄 Auto-reconnect with capped exponential backoff + jitter
  • 📱 Lifecycle aware — pauses local camera in background, resumes on foreground
  • 📡 Network quality scoring with transition events (excellent → critical)
  • 🪝 First-class React hooks (useRoomState, useLocalParticipant, …)
  • 🧪 Strict TypeScript, exact-optional, no any in public types
  • ⚖️ MIT licensed

Install

yarn add @levelchat/react-native react-native-webrtc
# or
npm install @levelchat/react-native react-native-webrtc

The react-native-webrtc peer dep ships native modules that need a native build; follow its install guide for iOS pods and Android gradle setup.

iOS additional setup

Add the privacy strings to Info.plist:

<key>NSCameraUsageDescription</key>
<string>This app uses the camera for video calls.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app uses the microphone for voice calls.</string>

For background audio (calls keep flowing when the user locks the screen) add audio under UIBackgroundModes. For screen sharing you also need a Broadcast Upload Extension target — see Apple's ReplayKit docs.

Android additional setup

In AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Screen sharing -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />

Request runtime permissions before joining a room — the SDK does not prompt on your behalf.

Quick start

import { useEffect, useState } from 'react';
import { View } from 'react-native';
import { RTCView } from 'react-native-webrtc';

import { LevelChat, type Room } from '@levelchat/react-native';
import { useParticipantViews, useRoomState } from '@levelchat/react-native/hooks';

const client = new LevelChat({ region: 'eu-fsn' });

export function CallScreen({ token, roomId }: { token: string; roomId: string }) {
  const [room, setRoom] = useState<Room | null>(null);
  const { connected } = useRoomState(room);
  const participants = useParticipantViews(room);

  useEffect(() => {
    let cancelled = false;
    client.joinRoom(roomId, { token }).then(async (r) => {
      if (cancelled) {
        await r.close();
        return;
      }
      setRoom(r);
      await r.publishMicrophone();
      await r.publishCamera({ resolution: '720p' });
    });
    return () => {
      cancelled = true;
      room?.close();
    };
  }, [roomId, token]);

  return (
    <View style={{ flex: 1 }}>
      {connected &&
        participants.map((p) => {
          const cam = p.tracks.find((t) => t.source === 'camera');
          if (!cam) return null;
          return (
            <RTCView
              key={p.id}
              streamURL={(cam.nativeTrack as { id: string }).id}
              style={{ width: 320, height: 180 }}
            />
          );
        })}
    </View>
  );
}

Token issuance

Production: mint room JWTs from your backend by calling POST /v1/rooms/:id/tokens with your project's API key. The mobile app should never see the API key.

Development: the SDK ships an issueToken helper that calls the same endpoint from the device. It refuses to run without apiKey: set on the client config and prints a docs link warning that this path is dev-only.

API surface

| Class / function | Purpose | | ---------------------- | ------------------------------------------------------------- | | LevelChat | Client config + factory; mints dev tokens, hands back Rooms | | Room | Room lifecycle + publish/subscribe | | LocalParticipant | The local user; tracks are LocalTracks | | RemoteParticipant | A remote user; tracks are RemoteTracks | | LocalTrack | Owns a producer; pause(), resume(), close() | | RemoteTrack | Owns a consumer; pause(), resume(), setPreferredLayer() | | enumerateDevices | Lists cameras + mics with normalised facing markers | | applyAudioMode | Manual override of the iOS/Android audio session | | setSpeakerphone | Toggle the loudspeaker route | | captureScreen | Acquire a screen-capture stream (iOS Picker / Android prompt) | | NetworkQualityScorer | Stand-alone scorer (scoreSample is a pure function) | | LifecycleObserver | AppState wrapper — emits paused / resumed |

Hooks (@levelchat/react-native/hooks)

| Hook | Returns | | -------------------------------- | --------------------------------------------------- | | useRoomState(room) | { state, connected } | | useLocalParticipant(room) | LocalParticipant \| null | | useRemoteParticipants(room) | RemoteParticipant[] | | useParticipantViews(room) | ParticipantView[] — local + remotes for rendering | | useParticipantTracks(room, id) | TrackView[] for one participant |

Errors

Every thrown value extends LevelChatError with a stable code. Apps should switch on code, not on message:

import { isLevelChatError } from '@levelchat/react-native';

try {
  await room.publishCamera();
} catch (err) {
  if (isLevelChatError(err) && err.code === 'media/permission-denied') {
    showPermissionPrimer();
  } else {
    showGenericError(err);
  }
}

The full list lives in src/errors.ts.

Limitations vs @levelchat/web

  • E2EE in-band frame encryption: not supported by react-native-webrtc yet. The library does not expose the RTCRtpScriptTransform (Web) or FrameCryptor (libwebrtc) APIs through the React Native bridge. We track this as an upstream blocker on the react-native-webrtc issue tracker. As a workaround the SDK ships the AES-GCM SFrame primitives (SFrame.encrypt / SFrame.decrypt + EncryptionContext) for apps that want to encrypt payloads before publishing — typical use cases: data-channel messages, out-of-band recording. The SDK accepts the e2ee: true join flag for API parity with the Web SDK and logs a warning at runtime that the in-band path falls back to SRTP. For end-to-end encrypted media flow today, use the Web SDK or the Android SDK (Android shipped the libwebrtc FrameCryptor binding in W2.9.1).
  • AV1: depends on the device — react-native-webrtc 124+ supports it on recent iOS/Android hardware. The SDK negotiates VP9/H.264 fallback automatically.
  • Live mode (@levelchat/web/live): not yet bridged. Use a regular Room with the broadcaster role for now.

Testing

pnpm --filter @levelchat/react-native test       # unit tests
pnpm --filter @levelchat/react-native typecheck  # tsc --noEmit
pnpm --filter @levelchat/react-native build      # emit dist/

Real device tests run via Detox / Maestro against the example app under ./example/.

License

MIT — see LICENSE.

See docs/api/02-sdk-parity.md for the cross-platform API parity matrix.