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.