diff --git a/.gitmodules b/.gitmodules
index 5056c22..6f03827 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,6 +2,9 @@
path = packages/web-client-sdk
url = https://github.com/fishjam-cloud/web-client-sdk.git
branch = mobile-sdk-2-0
+[submodule "packages/mobile-client-sdk"]
+ path = packages/mobile-client-sdk
+ url = https://github.com/fishjam-cloud/mobile-client-sdk.git
[submodule "packages/js-server-sdk"]
path = packages/js-server-sdk
url = https://github.com/fishjam-cloud/js-server-sdk.git
diff --git a/docs/how-to/client/migration-guide.mdx b/docs/how-to/client/migration-guide.mdx
new file mode 100644
index 0000000..fd25557
--- /dev/null
+++ b/docs/how-to/client/migration-guide.mdx
@@ -0,0 +1,538 @@
+---
+sidebar_position: 0
+sidebar_label: "Migration Guide"
+---
+
+# Migration Guide: From @fishjam-cloud/react-native-client to @fishjam-cloud/mobile-client
+
+This guide will help you migrate your React Native application from `@fishjam-cloud/react-native-client` to `@fishjam-cloud/mobile-client`.
+
+## Overview
+
+The new `@fishjam-cloud/mobile-client` package provides a more consistent API across web and mobile platforms, improved TypeScript support, and better integration with React's context system.
+
+## Package Installation
+
+### Before
+
+```bash
+npm install @fishjam-cloud/react-native-client
+```
+
+### After
+
+```bash
+npm install @fishjam-cloud/mobile-client
+```
+
+Remember to update all your imports:
+
+**From:**
+```tsx
+import { useCamera } from "@fishjam-cloud/react-native-client";
+
+```
+
+**To:**
+```tsx
+import { useCamera } from "@fishjam-cloud/mobile-client";
+```
+
+## Required Android Permissions
+
+Add the following WebRTC permissions to your Android configuration in `app.json`:
+
+```json
+{
+ "expo": {
+ "android": {
+ "permissions": [
+ "android.permission.CAMERA",
+ "android.permission.RECORD_AUDIO",
+ "android.permission.MODIFY_AUDIO_SETTINGS",
+ "android.permission.ACCESS_NETWORK_STATE",
+ "android.permission.ACCESS_WIFI_STATE"
+ ]
+ }
+ }
+}
+```
+## FishjamProvider Setup
+
+The new package requires wrapping your app with `FishjamProvider` to provide the Fishjam ID context.
+
+### Before
+
+```tsx
+import React from "react";
+// ---cut---
+import { useConnection } from "@fishjam-cloud/react-native-client";
+
+const { joinRoom } = useConnection();
+// fishjamId was passed directly to joinRoom
+await joinRoom({ fishjamId: "your-fishjam-id", peerToken: "..." });
+
+```
+
+### After
+
+```tsx
+import React from "react";
+import { Text } from "react-native";
+// ---cut---
+import { FishjamProvider } from "@fishjam-cloud/mobile-client"; // [!code highlight]
+
+// Wrap your app with FishjamProvider
+export default function App() {
+ return (
+ // [!code highlight]
+
+ // [!code highlight]
+ );
+}
+
+const YourApp = () => {return (<>Your app content>);};
+
+```
+
+The `fishjamId` is now provided through the context, so you no longer need to pass it to `joinRoom` or `useSandbox`.
+
+## Device Initialization
+
+### Before
+
+```tsx
+import { useCamera } from "@fishjam-cloud/react-native-client";
+
+const { prepareCamera } = useCamera();
+
+await prepareCamera({ cameraEnabled: true });
+
+```
+
+### After
+
+```tsx
+import { useInitializeDevices } from "@fishjam-cloud/mobile-client";
+
+const { initializeDevices } = useInitializeDevices();
+
+// Initialize devices when you first want to stream
+await initializeDevices({ enableAudio: false }); // or initializeDevices() for both
+
+```
+
+:::important
+On mobile, you should use `initializeDevices()` when you first want to stream. This gives your app access to all available devices and automatically requests camera and microphone permissions. After initialization, you can use `startCamera`/`stopCamera` or `startMicrophone`/`stopMicrophone` to manage device state.
+:::
+
+## Video Rendering and Accessing Peer Tracks
+Key changes:
+
+- `VideoRendererView` → `RTCView`
+- `trackId` prop → `streamURL` prop
+- Use `stream.toURL()` to get the URL from a MediaStream
+- Access peer video via `peer.cameraTrack.stream` instead of `peer.track`
+
+### Before
+
+```tsx
+import React from "react";
+import { View } from "react-native";
+// ---cut---
+import { usePeers, VideoRendererView } from "@fishjam-cloud/react-native-client";
+
+const { remotePeers, localPeer } = usePeers();
+
+const videoTracks = remotePeers.flatMap(
+ (peer) =>
+ peer.tracks.filter((track) => track.type === "Video" && track.isActive),
+ );
+const localTrack = localPeer?.tracks.find((t) => t.type === "Video");
+
+
+ {localTrack && (
+
+ )}
+ {videoTracks.map((track) => (
+
+ ))}
+
+```
+
+### After
+
+```tsx
+import React from "react";
+// ---cut---
+import { usePeers, RTCView } from "@fishjam-cloud/mobile-client";
+
+//TODO: FCE-2487 remove it when MediaStream will be updated
+interface MediaStreamWithURL extends MediaStream {
+ toURL(): string;
+}
+
+function VideoPlayer({ stream }: { stream: MediaStream | null | undefined }) {
+ const streamURL = stream ? (stream as MediaStreamWithURL).toURL() : null;
+
+ if (!streamURL) return null;
+
+ return (
+
+ );
+}
+
+const { localPeer, remotePeers } = usePeers();
+{localPeer?.cameraTrack?.stream && (
+
+)}
+
+{remotePeers.map((peer) => (
+ <>
+ {peer.cameraTrack?.stream && (
+
+ )}
+ >
+))}
+```
+
+Key changes:
+- `VideoRendererView` → `RTCView`
+- `trackId` prop → `streamURL` prop
+- Use `stream.toURL()` to get the URL from a MediaStream
+- `peer.tracks` array → `peer.cameraTrack` and `peer.microphoneTrack` properties
+- `track.isActive` removed (check if stream exists instead)
+- `track.type` removed (use specific track properties)
+
+## Connection API
+
+### Before
+
+```tsx
+import { useConnection, useSandbox } from "@fishjam-cloud/react-native-client";
+
+const { joinRoom } = useConnection();
+const { getSandboxPeerToken } = useSandbox({ fishjamId: "your-id" });
+
+const peerToken = await getSandboxPeerToken("roomName", "peerName");
+await joinRoom({ fishjamId: "your-id", peerToken });
+
+```
+### After
+
+```tsx
+import { useConnection, useSandbox } from "@fishjam-cloud/mobile-client";
+
+const { joinRoom } = useConnection();
+// fishjamId is now provided through FishjamProvider
+const { getSandboxPeerToken } = useSandbox();
+
+const peerToken = await getSandboxPeerToken("roomName", "peerName");
+await joinRoom({ peerToken }); // fishjamId passed through FishjamProvider
+
+```
+
+## Device Management
+
+### Camera Device Selection
+
+### Before
+
+```tsx
+import { useCamera } from "@fishjam-cloud/react-native-client";
+
+const { cameras, switchCamera, currentCamera } = useCamera();
+
+// find first camera facing opposite direction than current camera
+const otherCamera = cameras.find(
+ (camera) => camera.facingDirection !== currentCamera?.facingDirection,
+);
+if (otherCamera) {
+ switchCamera(otherCamera.id);
+}
+
+```
+
+### After
+
+```tsx
+import { useCamera } from "@fishjam-cloud/mobile-client";
+const { cameraDevices, selectCamera } = useCamera();
+
+// Select camera by deviceId
+const nextCamera = cameraDevices[0];
+if (nextCamera) {
+ selectCamera(nextCamera.deviceId);
+}
+
+```
+
+Key changes:
+
+- `cameras` → `cameraDevices`
+- `switchCamera` → `selectCamera` (takes `deviceId` instead of direction)
+- `facingDirection` property removed
+
+### Camera Control
+
+### Before
+
+```tsx
+import React from "react";
+import { Button } from "react-native";
+// ---cut---
+import { useCamera } from "@fishjam-cloud/react-native-client";
+const { isCameraOn, toggleCamera } = useCamera();
+
+
+
+```
+
+### After
+
+```tsx
+import { useCamera } from "@fishjam-cloud/mobile-client";
+const { startCamera, stopCamera, toggleCamera } = useCamera();
+
+// Start camera
+await startCamera();
+
+// Stop camera
+await stopCamera();
+
+// Toggle camera
+toggleCamera();
+
+```
+
+## Screen Sharing
+
+### Before
+
+```tsx
+import React, { useCallback } from "react";
+import { Button } from "react-native";
+// ---cut---
+import { useScreenShare } from "@fishjam-cloud/react-native-client";
+
+const { toggleScreenShare, isScreenShareOn } = useScreenShare();
+const onPressToggle = useCallback(
+ () => toggleScreenShare(),
+ [toggleScreenShare],
+ );
+
+
+
+```
+
+### After
+
+```tsx
+import React, { useCallback } from "react";
+import { Button } from "react-native";
+// ---cut---
+import { useScreenShare } from "@fishjam-cloud/mobile-client";
+
+const { startStreaming, stopStreaming, stream } = useScreenShare();
+
+const onPressToggle = () => {
+ if (stream) {
+ stopStreaming();
+ } else {
+ startStreaming();
+ }
+};
+
+
+
+```
+
+Key changes:
+
+- `toggleScreenShare()` → `startStreaming()` / `stopStreaming()`
+- `isScreenShareOn` → check if `stream` exists
+
+## Picture in Picture
+
+### Before
+
+```tsx
+import React from "react";
+import { View } from "react-native";
+// ---cut---
+import { PipContainerView } from "@fishjam-cloud/react-native-client";
+export function VideoCallScreen() {
+ return (
+
+ {/* Your video call UI */}
+
+ );
+}
+
+```
+
+### After
+
+```tsx
+import React, { useRef } from "react";
+import { Button } from "react-native";
+// ---cut---
+import { RTCPIPView, startPIP, stopPIP, usePeers } from "@fishjam-cloud/mobile-client";
+
+//TODO: FCE-2487 remove it when MediaStream will be updated
+interface MediaStreamWithURL extends MediaStream {
+ toURL(): string;
+}
+
+const pipViewRef = useRef>(null);
+const { localPeer } = usePeers();
+const stream = localPeer?.cameraTrack?.stream
+const streamURL = stream ? (stream as MediaStreamWithURL).toURL() : undefined;
+
+<>
+
+
+ // Manual control
+
+