livqenoDocs

GuidesHandle reconnection

Handle reconnection

What Livqeno does automatically when a network drops, what it cannot do, and the UI you still have to build.

In this guide you'll make your call handle network drops well.

Real users lose their network all the time: they walk out of Wi-Fi range, ride through a tunnel, switch from Wi-Fi to mobile data, or close a laptop lid. The Livqeno SDK reconnects on its own. Your job is the UI: telling the user what's happening, and not making a short blip look like the call ended.

Where this fits: you have a working call. This guide doesn't add a feature; it makes the one you have survive real networks. Do it before launch.

What you'll build

A call screen that, when the network drops:

  • Tells the user what's happening.
  • Keeps the video grid on screen.
  • Recovers on its own, without the user pressing anything.

What you need

Steps

1. Show the connection state

The SDK tracks the connection and tells you its state. Show it in your app; don't try to guess it from other signals. These are the five states:

type ConnectionState = 'connecting' | 'connected' | 'reconnecting' | 'disconnected' | 'failed';
room.on('connectionStateChanged', (state) => {
  banner.textContent = {
    connecting: 'Connecting…',
    connected: '',
    reconnecting: 'Connection lost — reconnecting…',
    disconnected: 'You have left the call.',
    failed: 'Could not reconnect.',
  }[state];
});

You should now see "Connection lost — reconnecting…" when you turn off Wi-Fi, and the banner clear when you turn it back on.

2. Keep the grid on screen

A common mistake: clearing the list of people when the state is reconnecting. During a reconnect, Livqeno keeps everyone in the room, and their tiles come back by themselves. If your grid empties and refills, a two-second blip looks to the user like everyone left the call.

// Do this
{state === 'reconnecting' && <ReconnectingOverlay />}
<ParticipantGrid />              {/* stays mounted */}
 
// Not this
{state === 'connected' ? <ParticipantGrid /> : <Spinner />}

3. Decide what to do on failed

failed means the SDK tried several times and gave up. The usual cause is a long outage. What happens next is up to you. First check why the room disconnected: if your backend removed the participant (REMOVED) or closed the room (ROOM_CLOSED), don't rejoin. The SDK never reconnects in those cases, and your app shouldn't either.

room.on('disconnected', (reason) => {
  if (reason === 'REMOVED' || reason === 'ROOM_CLOSED') showCallEnded(reason);
});

For the rest, a good default is to rejoin automatically with a new token from your backend:

room.on('connectionStateChanged', async (state) => {
  if (state !== 'failed') return;
 
  // A fresh token, because the old one may have expired while offline.
  const credentials = await fetch('/join-room', { method: 'POST' }).then((r) => r.json());
  const client = createRTCClient(credentials);
  const rejoined = await client.join(credentials.roomId);
  wireUp(rejoined);
});

Pass refreshToken and the SDK renews the token on its own, during the call and before a reconnect that would present an expired one, on every SDK: createRTCClient({ …, refreshToken }), new Livqeno({ …, refreshToken }) on React Native, Livqeno(refreshToken: …) on Flutter. See Keep long calls alive. You still need the rejoin above for a failed room.

4. Wait for the connection when you need it

join() gives you a room object quickly, but the media connection may still be starting. If your next step needs a live connection, wait for it:

const room = await client.join(roomId);
await room.waitUntilConnected(15_000);   // resolves on connect, rejects on timeout
await room.enableCamera();

If it doesn't connect within 15 seconds, the promise rejects, and you can show an error instead of a spinner that never ends.

5. Show connection quality

A reconnect is often preceded by a bad patch. Measuring packet loss (the share of media that didn't arrive) lets you warn the user early, for example "Your connection is unstable":

setInterval(async () => {
  const stats = await room.getConnectionStats();
  const worst = Math.max(0, ...stats.remote.map((t) => t.packetLossPercent ?? 0));
  setQuality(worst > 10 ? 'poor' : worst > 3 ? 'fair' : 'good');
}, 5000);

Warn users before the call drops.

Use the per-track numbers above, which are real measurements. Don't rely on stats.connectionQuality: today it usually says 'unknown', because the server doesn't compute it yet.

How it works

What happens when the network drops:

connectednetwork dropsreconnectingretries with growing gapsconnectedtracks resume
reconnectinggives up after several triesfailedyou rejoin
What the SDK does when the network drops.
  • Reconnecting is automatic and on by default (autoReconnect: true). The SDK retries with exponential backoff (it waits a little longer between each try, so thousands of clients don't all retry at once).
  • A short drop is resumed, not rebuilt. Each retry asks the server to resume the previous session. Within about two minutes the media server still has it, so media keeps flowing and nothing is renegotiated. After that, the SDK builds a new media connection and sends your camera and mic again.
  • A media-only failure is recovered too. If the media connection fails while signaling is fine, the SDK rejoins with a new media connection on its own, and you see reconnecting then reconnected.
  • Your tracks come back on their own. You don't need to turn the camera on again. That's why the reconnected event has no data.
  • The states are real. reconnecting means the connection really dropped. The SDK also receives the media server's own view of the connection, so getDiagnostics() may show "server says failed, browser says connected". That's useful information when debugging, not a bug.
  • A token expiring during a call only ends it where the server enforces expiry. The server warns the SDK a minute before; with refreshToken the SDK renews the token in place. Without it you get a TOKEN_EXPIRING warning, and, if enforcement is on, a disconnected with reason TOKEN_EXPIRED at expiry.

Production tips

  • TURN makes reconnecting work on real networks. A TURN relay passes media along when a network blocks a direct connection. Switching from Wi-Fi to cellular usually needs one. Livqeno includes TURN details in every token grant, in iceServers.
  • Use iceServers from a new token. TURN credentials expire with the token. Reusing an old iceServers list when rejoining is a common, confusing failure.
  • Our deploys close connections on purpose. Clients reconnect to another server within two seconds and resume their sessions, so media usually keeps flowing. To your users, a deploy looks like a brief reconnecting banner at most.
  • Test it for real. Turn Wi-Fi off for ten seconds. Reloading the page, or Chrome DevTools' offline toggle, doesn't test the same thing.

Next steps