Use this file to discover all available pages before exploring further.
This guide covers how to use the Prelude mobile SDKs to authenticate users on iOS, Android, Flutter, and React Native. The mobile SDKs persist tokens in the platform’s secure store and bind them to the device with DPoP.Pick your platform once: tabs across this section stay in sync.
A custom domain is optional on iOS. The SDK is reachable at the per-application Prelude subdomain (https://{app_id}.session.prelude.dev).
Android Studio with minSdk 26 or higher
A custom domain is optional on Android. The SDK is reachable at the per-application Prelude subdomain (https://{app_id}.session.prelude.dev).
iOS 15.1 or higher / Android API 26 or higher
The Dart API bridges to the native iOS (PreludeSession) and Android (so.prelude.android:session-sdk) SDKs, so tokens are persisted in the platform-native secure store and bound to the device with DPoP.
Expo SDK 54 or higher (or bare React Native 0.81+)
iOS 15.1+ / Android API 26+ on-device
The TypeScript API bridges to the native iOS (PreludeSession) and Android (so.prelude.android:session-sdk) SDKs, so tokens are persisted in the platform-native secure store and bound to the device with DPoP.
PreludeSessionClient is a Sendable value type — copies share a single underlying actor, so it’s safe to pass through @State, @Environment, or task groups.
PreludeSessionClient is thread-safe — its mutable state lives behind internal coordinators, so it’s safe to share a single instance across coroutines and ViewModels. Construct it off the main thread: instantiation hydrates the access-token cache from SharedPreferences, which is blocking I/O.
Each PreludeSessionClient instance represents a distinct session — DPoP keys, refresh tokens, and the access-token cache are scoped to that instance and managed by the native SDK. Most apps keep a single instance for the lifetime of the app.When you’re done with an instance, call dispose() to release the native client:
await client.dispose();
After dispose(), every other method on the instance throws StateError.
import { Endpoint, PreludeSessionClient,} from "@prelude.so/react-native-session-sdk";const client = new PreludeSessionClient({ endpoint: Endpoint.custom("https://{app_id}.session.prelude.dev"),});
Each PreludeSessionClient owns one logical session — the SDK forwards an opaque handle to the native side, which lazily creates one client per handle. Keep a single instance for the lifetime of your app (or per logical session).When you’re done with an instance, call dispose() to release the native client:
await client.dispose();
After dispose(), every other method on the instance throws DisposedError.
Try it
iOS
Android
Flutter
React Native
Create a new SwiftUI app in Xcode, add the PreludeSession package above, then replace ContentView.swift with:
ContentView.swift
import SwiftUIimport PreludeSession// Replace with your application id.private let appID = "YOUR_APP_ID"@MainActorfinal class SessionStore: ObservableObject { let client: PreludeSessionClient init() { self.client = try! PreludeSessionClient( endpoint: .custom("https://\(appID).session.prelude.dev") ) }}struct ContentView: View { @StateObject private var store = SessionStore() var body: some View { VStack(spacing: 12) { Text("Session Test").font(.title) Text("SDK initialized.") } .padding() }}
Build and run on the iOS simulator. If the SDK initialized cleanly, you’re ready to wire up a login flow.
Create a new Empty Compose app in Android Studio, add the dependency above, then replace MainActivity.kt with:
No additional helpers are required — the iOS examples on subsequent pages can be used as-is.
The Android Compose examples on subsequent pages call viewModel(factory = viewModelFactory(applicationContext)) to hand the application context to a ViewModel. Drop this small helper into your project and reuse it across the examples:
ViewModelFactory.kt
import android.content.Contextimport androidx.lifecycle.ViewModelimport androidx.lifecycle.ViewModelProviderfun viewModelFactory(context: Context) = object : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create(modelClass: Class<T>): T = modelClass.getConstructor(Context::class.java).newInstance(context) as T}
No additional helpers are required — the Flutter examples on subsequent pages can be used as-is.
No additional helpers are required — the React Native examples on subsequent pages can be used as-is.