Skip to content

React Native SDK Upgrade Guide

Every pre-8.0.0 React Native app must migrate. The npm package name remains @xmtp/react-native-sdk; version 8.0.0 uses the generated native v8 API. Existing method names, option shapes, generic types, hooks, and database files are not a compatibility contract.

Start with Migrate to a self-hosted backend. All participants must use the same backend. Keep the other SDK and backend version pins from that guide. Follow React Native and Expo to configure Hermes, New Architecture, native builds, and deployment floors.

Terminal window
npm install --save-exact @xmtp/react-native-sdk@8.0.0

Install iOS pods and rebuild the native app. Expo apps need a rebuilt development client or prebuilt native project. Expo Go and JavaScript-only updates cannot install the module. Import the public API from the package root and optional React helpers from /react. Do not import generated binding paths.

Areav8 integration
BackendPass backend: { url } or a Backend. Use the chosen deployment; there is no implicit public network.
SignerImplement async identity(), kind(), and sign(request), or restore a saved LocalSigningKey.
StoragePass explicit storage options. Keep the optional 32-byte encryption key, location, and label stable. Default storage uses the native app directory.
Client creationUse Client.create(signer, options) or reopen a current database with Client.build(identity, options).
ShutdownAwait client.end() before external file operations.
TypesUse Client, Group, Dm, Message, and Conversation without legacy generic parameters.
ValuesIDs are strings; bytes are Uint8Array; maps are Map; full-width integers use bigint; times use Timestamp.
ContentNarrow message.content.kind. Standard codecs are built in; register only app custom codecs in ClientOptions.codecs.
ReadersUse SDK message/conversation readers. Await app processing before the next value; close readers when their owner changes.
ReactUse XmtpProvider, useXmtp, and useClient from /react. No setClient, message hooks, or conversation hooks are exported.
AttachmentsUse client.attachments to create, upload, download, and manage native files. The app owns picking media and OS permissions.
PushObtain tokens and OS permissions in the app, then call client.enableNotifications. Rewrite native handlers for the current push payload.

The TypeScript shapes follow the Node native API, with mobile storage and lifecycle integration. Use the React Native API reference for the exact exported declarations. See Client, Signer, Streams, and Content types for checked React Native examples.

Convert closed legacy history to an archive

Section titled “Convert closed legacy history to an archive”

A package update does not open or adopt the old database. Keep a recoverable copy of the legacy database and its key. Close the legacy SDK before conversion. Create a new current database for the target backend and identity, then convert eligible history to an archive and import it:

import {
function prepareMigrationArchive(args: PrepareMigrationArchiveArgs): Promise<MigrationReport>
prepareMigrationArchive
} from "@xmtp/react-native-sdk";
import type {
class Client

An XMTP client. Its inputs and results are public values; the binding Client and the target transport stay private to the package.

Client
} from "@xmtp/react-native-sdk";
const
const archive: MigrationReport
archive
= await
function prepareMigrationArchive(args: PrepareMigrationArchiveArgs): Promise<MigrationReport>
prepareMigrationArchive
({
databasePath: string
databasePath
,
databaseKey?: Uint8Array<ArrayBufferLike> | undefined
databaseKey
,
archiveKey: Uint8Array<ArrayBufferLike>
archiveKey
,
outputPath: string
outputPath
,
});
await
client: Client
client
.
ClientMembers.archives: Archives
archives
.
Archives.importFromFile(path: string, keyBytes: Uint8Array): Promise<void>
importFromFile
(
const archive: MigrationReport
archive
.
archivePath: string
archivePath
,
archiveKey: Uint8Array<ArrayBufferLike>
archiveKey
);

The native converter accepts legacy databases whose recorded migrations match the checked legacy migration chain, including known prefixes it can advance. The pinned chain comes from libxmtp 6a8e969785c62e0923cf3d8531aff4eaac3c91b7: 64 migrations used by Node 6.1.0 and Browser 7.1.0; Android and iOS 4.10.0 have 62, and 4.11.0 have 64. A React Native package version alone does not prove schema compatibility. Unknown, reordered, or changed migrations fail with MigrationUnsupportedSchema. Retain the original database when conversion fails. Do not describe this as support for every old React Native release.

Conversion works on a copy of a closed source and preserves the source. A source lock produces StorageBusy; close every legacy connection first. The archive contains eligible messages, conversation metadata, and consent. It excludes installation keys, MLS secrets, attachment files, and messages with stored expiry deadlines. Import does not restore the old installation or move its network identity. Arrange account registration and conversation membership on the new backend separately.

Supply a separate 32-byte archive key and an output path in app-accessible storage. Keep the archive key separate from the archive file. Do not overwrite the source with the output.

Only import archives from a trusted source. Import does not verify message provenance or sender membership, and anyone with the key can forge history under another sender. Legacy repeated-nonce archives have weakened confidentiality and authentication; importing them cannot restore those guarantees.

See Backups and the migration overview for conversion errors, archive limits, and restoration behavior.

The provider releases clients it creates and preserves caller-owned clients passed through its client prop. Do not close a borrowed client in a hook cleanup unless that component is its actual owner. disconnect() cancels a pending initialization; a late result cannot publish into context. Handle initialization and cleanup errors explicitly.

Use native lifecycle handling supplied by the SDK. A JavaScript AppState listener cannot cover native cold starts or work after runtime teardown. Background push and iOS extensions require native OS integration, bounded sync, and coordinated access to app storage. See Push notifications before shipping the receiver.