Skip to content
Ownfold
Esc
navigateopen⌘Jpreview
On this page

React API reference

Complete reference for Ownfold's SSR-safe provider and every headless encryption, recovery, device, pairing, and rotation hook.

@ownfold/react is strictly headless. It renders no controls, supplies no text, and ships no CSS. Your application decides the name of the feature, its description, which lifecycle actions are visible, and how every state is presented.

pnpm add @ownfold/browser @ownfold/react

Add the provider

Create one browser client and provide it above the application-owned controls.

"use client"

import { VaultProvider } from "@ownfold/react"
import type { ReactNode } from "react"
import { vault } from "@/lib/vault-client"

export function EncryptionProvider({ children }: { readonly children: ReactNode }) {
  return (
    <VaultProvider client={vault} onInitializationError={reportSafeVaultError}>
      {children}
    </VaultProvider>
  )
}

VaultProvider is SSR-safe. Initialization runs after hydration by default. Set initialize={false} only when the host owns the initialization lifecycle. onInitializationError receives a typed OwnfoldError; do not log secrets, ciphertext, or decrypted values with it.

Provider behavior

Event Behavior
Server render Provides the caller-owned client through context without running initialization or another browser side effect.
First client effect Calls client.initialize() when initialize is not false.
Initialization success Client state subscription updates every consuming hook.
Initialization error Calls onInitializationError when supplied; the typed client state remains authoritative.
Client prop replacement Unsubscribes from the previous client and binds the new instance. The provider does not dispose caller-owned clients.
Unmount Removes React subscriptions. It does not delete vault storage or revoke a device.

Every hook throws a framework-boundary error when called outside VaultProvider. This programmer error is distinct from expected Ownfold operation failures, which remain Result values.

Hook reference

Hook Values and operations Use it when
useVault() The current VaultClient. The host needs lifecycle methods not wrapped by another hook, such as createVault() or destroyLocalState().
useVaultStatus() The current discriminated VaultState. Rendering controls from explicit lifecycle states.
useVaultLock() state, lock(), unlock(secret?). Adding lock and unlock controls.
useRecoveryKit() create, get, generateCode, restore, verify, prepareReplacement, completeReplacement, getPendingReplacement. Building onboarding, restore, or replacement flows.
useVaultDevices() current, list, revoke. Showing and revoking enrolled browsers.
useVaultPairing() begin, refresh, approve, cancel. Building an existing-device approval flow.
useKeyRotation(records, options?) prepare, complete, getPendingRecoveryKit. Rotating the root key after the host supplies a RotationRecordStore.
useKeyRotationProgress() A discriminated rotation progress snapshot. Showing resumable rotation phases and processed-record counts.

Every fallible operation preserves the browser client’s typed Result value. Hooks never turn an error into a success-shaped fallback and never download a file as a side effect.

Exact hook contracts

useVault()

Returns the provider’s stable VaultClient identity. Use it for createVault, encryptJson, decryptJson, binary operations, subscriptions, or less common lifecycle methods. Do not call dispose() on a shared provider client from a child component.

useVaultStatus()

Subscribes with React’s external-store contract and returns the current VaultState. Components rerender when the state object changes. Switch on status; do not copy the state into a second local state machine.

useVaultLock()

Member Signature Behavior
state VaultState Same reactive snapshot as useVaultStatus.
lock () => VaultClientResult<VaultState> Synchronously destroys active root-key access and broadcasts the lock.
unlock (secret?: string) => Promise<VaultClientResult<VaultState>> Opens the enrolled device envelope, or uses recovery only in a state that supports the optional secret.

useRecoveryKit()

Member Result Side effect boundary
generateCode() Result<string, CryptoEngineError> Generates a local secret; does not persist or display it.
create(secret) RecoveryKitV1 Creates pending local/remote vault metadata; does not download.
get() RecoveryKitV1 Reads the locally retained onboarding kit.
verify(contents, secret) VaultState Parses and opens the imported file, compares root keys, then enrolls the device.
restore(contents, secret) VaultState Restores an unavailable device from an existing kit.
prepareReplacement(secret) RecoveryKitV1 Creates a pending replacement without changing the authoritative kit.
getPendingReplacement() `PendingRecoveryKitReplacement null`
completeReplacement(contents, secret) VaultState Verifies the downloaded replacement before committing metadata.

useVaultDevices()

Member Result
current() Current local device ID or null.
list() Remote-safe readonly DeviceSummary[].
revoke(deviceId, expectedRevision) Updated DeviceSummary.

The hook does not ask for confirmation. The application owns confirmation UI and must refresh after a revision conflict. Revoking the current device removes its future remote authorization; local lock/state behavior still follows the client lifecycle.

useVaultPairing()

Member Result Intended device
begin() PairingOfferV1 New device creates and shares the offer.
refresh() VaultState New device polls/reconciles request status.
approve(offer) PairingRequest Existing unlocked device parses the offer and creates the device envelope.
cancel() VaultState New device cancels its current pending request.

useKeyRotation(records, options?)

records implements RotationRecordStore. options.batchSize controls records per persisted checkpoint; it does not weaken server transaction requirements.

Member Result
prepare(secret) Replacement RecoveryKitV1 for download and re-import.
getPendingRecoveryKit() Pending kit or null after reload.
complete(contents, secret) Final VaultState after kit verification, record rewrap, device envelope replacement, and server cutover.

useKeyRotationProgress()

Returns RootKeyRotationProgress, including phase, processed count, and total when known. It subscribes independently from general vault state so progress UI updates during long batches without inventing a second polling loop.

Build only the controls you need

import { useVault, useVaultLock, useVaultStatus } from "@ownfold/react"

export function PrivacySettings() {
  const vault = useVault()
  const state = useVaultStatus()
  const { lock, unlock } = useVaultLock()

  return (
    <section aria-labelledby="privacy-heading">
      <h2 id="privacy-heading">Private workspace</h2>
      <p>Encryption state: {state.status}</p>

      {state.status === "not-created" && (
        <button type="button" onClick={() => void vault.createVault()}>
          Turn on encryption
        </button>
      )}
      {state.status === "ready-unlocked" && (
        <button type="button" onClick={() => lock()}>Lock</button>
      )}
      {state.status === "ready-locked" && (
        <button type="button" onClick={() => void unlock()}>Unlock this browser</button>
      )}
    </section>
  )
}

The example deliberately owns the heading, description, button labels, and state visibility. Rename or omit any of them without configuring Ownfold because presentation is not part of the SDK interface.

Recovery Kit files

useRecoveryKit().create(secret) returns a RecoveryKitV1; it does not download it. The host can use RecoveryKitFile.download(result.value) or its own file workflow. Re-import the actual saved file before calling verify(contents, secret) so onboarding proves the user’s recovery artifact.

const recovery = useRecoveryKit()
const created = await recovery.create(secret)
if (created.status === "error") return showError(created.error)

RecoveryKitFile.download(created.value, "my-product-recovery.json")

const contents = await selectedFile.text()
const verified = await recovery.verify(contents, secret)
if (verified.status === "error") return showError(verified.error)

generateCode() produces a cryptographically random recovery code for applications that prefer a generated secret. get() reads the locally retained kit. Replacement remains a two-step prepare/download/import/complete flow; getPendingReplacement() lets the host resume it after a reload.

Pairing, devices, and rotation

Pairing offers are application data. Render them as text, a host-selected QR library, a share sheet, or another channel appropriate to the product. Ownfold provides only the typed offer and actions.

Device list entries contain metadata such as labels and last-active timestamps. The application chooses which fields to show and must confirm destructive revocation actions itself.

Rotation is opt-in because Ownfold cannot discover application records. Pass the host’s RotationRecordStore to useKeyRotation; download and re-import the replacement Recovery Kit before complete. Use useKeyRotationProgress to render the phases that matter to the product.

Security constraints

  • Never send recovery passwords, imported Recovery Kits, or decrypted data to analytics, logs, server actions, or error reporting.
  • Render from the discriminated state instead of enabling actions in invalid states.
  • Preserve typed errors and tell the user what remains safe and what recovery action to take.
  • Treat automatic locking as key-lifetime reduction, not protection from malicious same-origin JavaScript.

See the browser client reference for every lifecycle method and state, and Accessibility for requirements that now belong to the host interface.

Last updated on August 4, 2026

Was this page helpful?