Lifecycle
One session owns account selection, graceful client replacement, logout, and recovery.
A Jazz session owns the selected account and its client. Configure it once; use session commands to link, log in, log out, or restore an account. Each underlying context remains bound to one immutable account handle.
Starting a session
import { createJazzSession } from "jazz-tools/client";
const jazz = await createJazzSession({
appId: "my-app",
serverUrl: "https://core.example",
initial: "local-first",
});
const { client, account } = jazz.getSnapshot();initial: "local-first" restores usable saved selection or creates a local-first account when none exists. Without it, an empty selection stays signed out. External identities are never registered implicitly. Browser account storage retains local signing roots and selection; provider JWTs are not persisted.
Linking and switching accounts
await jazz.linkJWT({ getToken }); // Add an identity to this account.
await jazz.loginJWT({ getToken }); // Select an already registered identity.
await jazz.registerJWT({ getToken }); // Explicitly register a fresh identity.For each transition Jazz hides the old client, waits for framework consumers to detach, then performs ordinary graceful shutdown with waitForSync: true. Only afterward does it perform the account operation, outside any context, and open the replacement client. Earlier rows retain their original issuer and subject. Linking preserves the stable account ID and its data.
A sync-barrier failure leaves the old client usable and prevents the account operation. If enrollment fails after shutdown, Jazz reopens the previous selection when possible. If enrollment succeeds but client startup fails, the new selection remains and await jazz.retry() retries startup without repeating enrollment. A teardown failure after sync is reported with no usable client; Jazz does not pretend a stopped context is ready.
Overlapping account-changing commands reject with a busy error instead of silently applying to a later account. Logout and close supersede in-flight work and prevent late completions from publishing a client. A remote link that already succeeded remains discoverable by a later explicit login.
Reactive state
getSnapshot() and subscribe(listener) expose stable snapshots. Status is ready, signed-out, transitioning, error, or closed. A client is available only in ready. A recoverable operation error may accompany ready, so the app can show it while the previous account remains usable.
import { JazzSessionProvider, useJazzSession } from "jazz-tools/react";
function App() {
return (
<JazzSessionProvider
config={{ appId, serverUrl, initial: "local-first" }}
fallback={<AccountStatus />}
>
<TodoApp />
</JazzSessionProvider>
);
}
function AccountStatus() {
const { status, error, retry } = useJazzSession();
return error ? (
<button onClick={() => void retry().catch(console.error)}>{error.message}: retry</button>
) : (
<p>{status === "signed-out" ? "Sign in to continue" : "Loading…"}</p>
);
}The hook is available in children and fallback. Data consumers unmount during a transition; Jazz handles the shutdown/replacement sequence. An app can put sign-in UI in the signed-out fallback and invoke loginJWT or registerJWT there. Config is captured at mount; use a React key to replace an application's configuration. Passing an existing session instead of config leaves ownership with its caller: unmounting the provider does not close it.
For authentication coordination that must remain mounted while the data UI changes, useJazzSessionOwner(config) returns { session, error, retry }. It owns initialization and cleanup with the same lifecycle as the configured provider. Pass the resulting session to JazzSessionProvider; keep provider-specific login/sign-out state above that provider. No app-owned shutdown queue or unmount timer is needed.
React Native and Expo expose the same provider/hook from their platform entrypoints. Svelte provides JazzSessionProvider, getJazzSession() (a store with bound commands), and sessionState(session). Vue and Solid provide providers and reactive session adapters over the same state machine.
Logout and disposal
await jazz.logout();
await signOutFromYourProvider();
// For apps that intentionally return to a fresh anonymous account:
await jazz.createLocalFirst();Logout stays signed out until the app chooses another account. It does not erase the local database or revoke an identity at core. Provider cookies remain the provider's responsibility. Local-first signing roots remain available for recovery.
Call await jazz.close() when retiring an imperatively owned session. Ordinary close preserves local durability and works offline; it does not promise remote sync completed. Account-changing commands use the stronger sync barrier automatically.
Recovery phrases and passkeys
import { exportLocalFirstSecret } from "jazz-tools";
import { RecoveryPhrase } from "jazz-tools/passphrase";
const secret = exportLocalFirstSecret(jazz.getSnapshot().account!);
const phrase = RecoveryPhrase.fromSecret(secret);
await jazz.restoreLocalFirst(RecoveryPhrase.toSecret(recoveredPhrase));Use the same secret with BrowserPasskeyBackup for a passkey backup. Export requires a live local-first handle; external or logged-out handles cannot export it. Secrets never appear in snapshots. Possession of a recovery phrase allows acting as that local-first identity.
Lower-level composition
createAccountManager, opaque AccountHandle, and createJazzClient remain available for applications that intentionally own their lifecycle. The manager handles credentials and registry operations only. Contexts accept valid handles and never change identity; low-level callers must arrange graceful shutdown before linking or replacing contexts themselves. The session abstraction composes these primitives in shared TypeScript; framework adapters only observe state and acknowledge consumer cleanup.
Storage reset
await db.deleteClientStorage() resets the active browser IndexedDB namespace and keeps the live client usable. It permanently deletes unsynced writes and local-only data. Resync requires the data to already exist on the configured server and the same account to retain access. Ordinary close or shutdown is not a server-sync receipt.
Reset preserves default local-first signing roots in localStorage and does not sign out the account or external auth provider. Preserve browser credentials and other databases; verify custom credential stores separately. Logout changes the selected account state, while a storage reset clears the selected data cache. Reset does not register or link an account.
The browser console helper provides the same scoped reset and requires a live registered client. Keep the client open until the reset finishes.