---
title: "Upgrade to v3 | Micdrop"
description: "Upgrade a Micdrop app to v3, where the client was split so the same call runs in a browser and on a phone. Breaking changes, version mapping and what stays put."
url: "https://micdrop.dev/docs/migration/v3"
---

*   [Getting Started](/docs/getting-started)
*   [Client (Browser)](/docs/client)
    
    *   [Installation](/docs/client/installation)
    *   [React Hooks](/docs/client/react-hooks)
    *   [Start/Stop Call](/docs/client/start-stop-call)
    *   [Pause/Resume Call](/docs/client/pause-resume-call)
    *   [Mute/Unmute Call](/docs/client/mute-unmute-call)
    *   [Call State](/docs/client/call-state)
    *   [Display Conversation Messages](/docs/client/display-conversation-messages)
    *   [Handling Tool Calls](/docs/client/handling-tool-calls)
    *   [Device Management](/docs/client/devices-management)
    *   [Voice Activity Detection (VAD)](/docs/client/vad)
    *   [Error Handling](/docs/client/error-handling)
    *   Utility Classes
        
        *   [Mic](/docs/client/utility-classes/mic)
        *   [MicdropClient](/docs/client/utility-classes/micdrop-client)
        *   [MicRecorder](/docs/client/utility-classes/mic-recorder)
        *   [Speaker](/docs/client/utility-classes/speaker)
        
    
*   [Client (React Native)](/docs/react-native)
    
    *   [Installation](/docs/react-native/installation)
    *   [Hooks and Call State](/docs/react-native/hooks)
    *   [Audio Output and Devices](/docs/react-native/audio-output)
    *   [Voice Activity Detection (VAD)](/docs/react-native/vad)
    *   [Using Another Audio Library](/docs/react-native/custom-audio)
    
*   [Server (Node.js)](/docs/server)
    
    *   [Installation](/docs/server/installation)
    *   [With Fastify](/docs/server/with-fastify)
    *   [With NestJS](/docs/server/with-nestjs)
    *   [Auth and Parameters](/docs/server/auth-and-parameters)
    *   [First Message](/docs/server/first-message)
    *   [Save Messages](/docs/server/save-messages)
    *   [Resume a Conversation](/docs/server/resume-conversation)
    *   [Recording Audio](/docs/server/recording-audio)
    *   [Error Handling](/docs/server/error-handling)
    *   [Tools](/docs/server/tools)
    *   [Extract Value from Answer](/docs/server/extract)
    *   [Auto End Call](/docs/server/auto-end-call)
    *   [Semantic Turn Detection](/docs/server/semantic-turn-detection)
    *   [Noise Filtering](/docs/server/noise-filtering)
    *   [Micdrop Protocol](/docs/server/protocol)
    
*   [AI Integrations](/docs/ai-integration)
    
    *   Provided Integrations
        
        *   [AI SDK](/docs/ai-integration/provided-integrations/ai-sdk)
        *   [Cartesia](/docs/ai-integration/provided-integrations/cartesia)
        *   [ElevenLabs](/docs/ai-integration/provided-integrations/elevenlabs)
        *   [Gladia](/docs/ai-integration/provided-integrations/gladia)
        *   [Gradium](/docs/ai-integration/provided-integrations/gradium)
        *   [Kokoro](/docs/ai-integration/provided-integrations/kokoro)
        *   [Mistral](/docs/ai-integration/provided-integrations/mistral)
        *   [OpenAI](/docs/ai-integration/provided-integrations/openai)
        *   [Piper](/docs/ai-integration/provided-integrations/piper)
        *   [Pocket TTS](/docs/ai-integration/provided-integrations/pocket-tts)
        *   [Whisper](/docs/ai-integration/provided-integrations/whisper)
        
    *   Custom Integrations
        
        *   [Agent (LLM)](/docs/ai-integration/custom-integrations/custom-agent)
        *   [Speech-to-Text (STT)](/docs/ai-integration/custom-integrations/custom-stt)
        *   [Text-to-Speech (TTS)](/docs/ai-integration/custom-integrations/custom-tts)
        
    *   Fallback Strategies
        
        *   [FallbackAgent](/docs/ai-integration/fallback-strategies/agent-fallback)
        *   [FallbackSTT](/docs/ai-integration/fallback-strategies/stt-fallback)
        *   [FallbackTTS](/docs/ai-integration/fallback-strategies/tts-fallback)
        
    *   [Local Models](/docs/ai-integration/local-models)
        
        *   [Choosing the Models](/docs/ai-integration/local-models/choosing-models)
        *   [Latency and Memory](/docs/ai-integration/local-models/performance)
        *   [Explorations](/docs/ai-integration/local-models/explorations)
        
    *   [IA Vocale Souveraine 🇫🇷🇪🇺](/docs/ai-integration/sovereign-voice-ai)
    
*   [Migration](/docs/migration)
    
    *   [Upgrade to v3](/docs/migration/v3)
    

[Micdrop](/) › [Documentation](/docs/getting-started)

# Upgrade to v3

_Released in August 2026._

v3 splits the client in two. Everything that runs a call, the protocol, the state, the voice activity detection and the recording, moved into a platform agnostic `@micdrop/client`. The browser code that surrounds it became `@micdrop/web`, and [`@micdrop/react-native`](/docs/react-native) is the same call on iOS and Android.

For most apps the upgrade is one install and one search and replace.

## Which versions go together

Package

v2

v3

`@micdrop/web`

`1.x`

new, this is what a browser app installs

`@micdrop/react-native`

`0.x`

new, for iOS and Android

`@micdrop/client`

`2.x`

`3.x`

no longer installed directly, it arrives as a dependency

`@micdrop/react`

`1.x`

`2.x`

now works on both platforms

`@micdrop/server`

`2.x`

`2.x`

unchanged, nothing to do

The server does not move. The protocol between the two sides is the same, so a v3 client talks to the server you are already running, and you can upgrade one side at a time.

The AI packages, `@micdrop/openai` and the others, do not move either. They sit on the server and never saw the client.

## The short version

Terminal window

```
npm uninstall @micdrop/clientnpm install @micdrop/web
```

```
import { Micdrop } from '@micdrop/client'import { Micdrop } from '@micdrop/web'
```

`@micdrop/web` re-exports the whole shared API, so every name you imported before still comes from this single package. You never install `@micdrop/client` yourself, it arrives as a dependency and follows the browser package.

If you use the React hooks, upgrade them too:

Terminal window

```
npm install @micdrop/react@^2
```

`@micdrop/react` v2 declares `@micdrop/client` v3 as a peer, so npm says so if the two drift apart. Kept together by force, `useMicVolume` and `useSpeakerVolume` subscribe to a `Volume` event that v2 never emits, and the level meters stay flat. The other four hooks would still work.

## What breaks

### Devices are one shape on every platform

A phone does not have `MediaDeviceInfo`, so devices became a small type of their own. The values are the same, the field names are shorter.

```
<option key={device.deviceId} value={device.deviceId}>  {device.label || 'Microphone'}<option key={device.id} value={device.id}>  {device.label}</option>
```

`MicdropDevice` carries `id`, `label` and `kind`. The label is never empty now, it falls back to a generic name when the browser hides it.

### The speaker takes samples rather than a Blob

`Speaker.playAudio()` used to accept the `Blob` the WebSocket delivered. The connection now reads binary as `ArrayBuffer`, which skips a conversion on every chunk, so the speaker takes the samples directly.

```
const audioBlob = await response.blob()Speaker.playAudio(audioBlob)Speaker.playAudio(await response.arrayBuffer())
```

`Int16Array` works too. Nothing changes for a normal call, where the client feeds the speaker on its own.

### Levels come from events, not from an analyser

`Mic.analyser` and `Speaker.analyser` are gone, along with the `AudioAnalyser` class. The level is now an event, measured the same way on both platforms.

```
Mic.analyser.on('volume', onVolume)Mic.on('Volume', onVolume)
```

The number means exactly what it meant before, the loudest frequency in decibels, so a threshold you had tuned still holds.

For a raw `AnalyserNode`, connect one to the node the assistant voice comes out of:

```
import { audioContext, getSpeakerOutput } from '@micdrop/web'
const analyser = audioContext.createAnalyser()getSpeakerOutput()?.connect(analyser)
```

### The microphone no longer hands back a stream

`Mic.start()` returns nothing. A phone has no `MediaStream`, and the recorder reads the microphone through the shared object instead.

```
const stream = await Mic.start()await recorder.start(stream)await Mic.start()await recorder.start(Mic)
```

### Silero is an opt-in import

The ONNX runtime weighs more than the rest of Micdrop put together, so it only enters your bundle when you ask for it. Add one import, wherever your app starts:

```
import '@micdrop/web/silero'
```

Without it, `vad: 'silero'` throws with a message saying exactly this. `'volume'` needs nothing.

### Renamed

v2

v3

`LocalStorageKeys`

`MicdropStorageKeys`

`state.micDevices: MediaDeviceInfo[]`

`state.micDevices: MicdropDevice[]`

The stored keys themselves are unchanged, so a returning user keeps the microphone, the speaker and the VAD settings they had.

### Errors say what went wrong

A missing `url` and a refused microphone used to surface as `MicdropClientErrorCode.Connection`, because they were caught and relabelled on the way out. They now arrive as `MissingUrl` and `Mic`. Code that branched on `Connection` to cover those cases needs the other two branches.

## What does not change

*   **The protocol.** A v3 client talks to a v2 server, and the other way round. `@micdrop/server` did not move.
*   **The call API.** `start`, `stop`, `mute`, `pause`, `StateChange`, the conversation, the tool calls: all identical.
*   **Saved settings.** Same storage keys, same values.
*   **VAD thresholds.** The level is measured exactly as before, so your tuned numbers carry over.

## What you gain

*   The same call on [iOS and Android](/docs/react-native), against the server you already run
*   [React hooks](/docs/client/react-hooks) that work on both, from one package
*   Silero on a phone as well as in a browser, sharing one implementation
*   A first syllable that no longer gets cut: each VAD now declares how long it takes to notice speech, and that much audio is kept in reserve
*   A warning when a microphone announces one sample rate and delivers another, which used to turn into unexplained transcription errors

[Previous← Migration](/docs/migration)

On this page

*   [Which versions go together](#which-versions-go-together)
*   [The short version](#the-short-version)
*   [What breaks](#what-breaks)
*   [Devices are one shape on every platform](#devices-are-one-shape-on-every-platform)
*   [The speaker takes samples rather than a Blob](#the-speaker-takes-samples-rather-than-a-blob)
*   [Levels come from events, not from an analyser](#levels-come-from-events-not-from-an-analyser)
*   [The microphone no longer hands back a stream](#the-microphone-no-longer-hands-back-a-stream)
*   [Silero is an opt-in import](#silero-is-an-opt-in-import)
*   [Renamed](#renamed)
*   [Errors say what went wrong](#errors-say-what-went-wrong)
*   [What does not change](#what-does-not-change)
*   [What you gain](#what-you-gain)
