Capxul Docs
Hooks

CapxulProvider

The root provider that bootstraps the Capxul client and owns the TanStack Query cache for every hook beneath it.

Wraps your app once and makes every @capxul/sdk-react hook work beneath it. The provider owns the client lifecycle and the TanStack Query cache — read bootstrap progress with useCapxul, and reach the raw client (rarely needed) with useCapxulClientOrNull.

Import

import { CapxulProvider } from "@capxul/sdk-react";

Usage

"use client";

import { CapxulProvider } from "@capxul/sdk-react";

export function App() {
  return (
    <CapxulProvider
      publishableKey={process.env.CAPXUL_PUBLISHABLE_KEY!}
      requirement="deployed"
    >
      <Routes />
    </CapxulProvider>
  );
}

That is the whole setup for a browser app: one publishable key, no other wiring. You do not call createCapxulClient, mount a QueryClientProvider, or build a signer module — the provider does all of it.

Modes

The provider has two mutually exclusive modes, selected by which prop you pass. Supply exactly one of publishableKey or client; passing both or neither throws at render time.

Bootstrap mode (publishableKey)

The path for browser apps. The provider runs the async createCapxulClient({ publishableKey, requirement, signer }) bootstrap itself, exposes progress through useCapxul, and closes the client on unmount. Changing publishableKey, requirement, or signer — or calling retry() from useCapxul — re-bootstraps: the old client is closed and a new one is created.

While the bootstrap is in flight, data hooks simply sit in isPending. A plain app needs no bootstrap awareness at all; a splash screen is opt-in via useCapxul.

Injected client mode (client)

The path for Node and server consumers that bootstrap before React, and for test harnesses. You build the client yourself with createCapxulClient from @capxul/sdk and hand it in; the provider is ready immediately and never closes that client — its lifecycle stays with you.

import { createCapxulClient } from "@capxul/sdk";
import { CapxulProvider } from "@capxul/sdk-react";

const result = await createCapxulClient({
  publishableKey: process.env.CAPXUL_PUBLISHABLE_KEY!,
});
if (!result.ok) throw result.error;

function Root() {
  return (
    <CapxulProvider client={result.value}>
      <Harness />
    </CapxulProvider>
  );
}

requirement and signer are not accepted in this mode — they are inputs to the bootstrap you already ran.

Props

The exported prop type is a union of the two modes:

import { type CapxulProviderProps } from "@capxul/sdk-react";

publishableKey

string

Selects bootstrap mode. The provider passes it to createCapxulClient and manages the resulting client. Mutually exclusive with client.

client

CapxulClient

Selects injected client mode: a pre-built client whose lifecycle stays with the caller. Mutually exclusive with publishableKey. Swapping the prop to a different client instance clears the capxul-scoped queries so no data leaks across clients.

requirement

AccountRequirement | undefined — bootstrap mode only. Default "none".

The init-time account readiness target. When it is not "none", account setup starts automatically after sign-in — your app only calls useCapxulVerifyOtp and reads progress with useCapxulAccountLifecycle.

  • "none" (default) — no readiness target; nothing is provisioned automatically.
  • "counterfactual" — the Account's address is reserved so money can be addressed to it, without full setup.
  • "deployed" — the Account is fully set up and ready to move money.

signer

CapxulSigner | undefined — bootstrap mode only.

An optional consumer-held signer for the deploy lane. Browser apps with requirement: "deployed" omit it — the SDK resolves what it needs from bootstrap and wires the embedded signer automatically. Pass one only when your app holds its own signer (for example a script or server flow that deploys with its own key material).

queryClient

QueryClient | undefined

Bring your own TanStack QueryClient if your app already has one; otherwise the provider creates and owns one. The value is pinned at mount — a later queryClient prop swap is ignored, so pass it once or not at all. See Query cache ownership for how ownership changes re-bootstrap behavior.

children

ReactNode

Your app tree. Every useCapxul* hook must render beneath the provider.

Query cache ownership

The provider renders the QueryClientProvider itself — consumers never mount one for Capxul hooks. When the provider creates the QueryClient, it applies these defaults:

  • queries: retry: 2, staleTime: 30_000 (30 seconds)
  • mutations: retry: 0

A queryClient you pass in keeps its own defaults — the provider does not modify it.

On re-bootstrap (prop change or retry()) and whenever the client instance changes, stale data from the previous client is dropped:

  • provider-owned QueryClient — the entire cache is cleared;
  • your own QueryClient — only queries whose key starts with "capxul" are removed, so the rest of your app's cache is untouched.

This is why custom queries built on useCapxulClientOrNull should use ["capxul", ...] keys.

Errors

Passing both publishableKey and client, or neither, throws at render time:

CapxulProvider requires exactly one of `publishableKey` or `client`

A failed bootstrap does not throw. The provider moves to status: "error" and exposes the CapxulError plus a retry() function through useCapxul; data hooks stay in isPending until a retry succeeds.

On this page