React SDK
Import from wattfare/react. A provider that owns one client, and a useWattfare() hook that exposes reactive connection state and the connect / disconnect / refresh actions.
<WattfareProvider>
Wrap your app (or the subtree that needs budget state) once. It creates a single
WattfareClient via createWattfare() and memoizes it — stable across
re-renders, only re-created when its config changes.
import { WattfareProvider } from "wattfare/react";
<WattfareProvider
publishableKey="pk_live_…" // required
session="/api/wattfare-token" // required: route path or token fn
baseURL="https://wattfare.com" // optional
consentURL="https://wattfare.com" // optional
loadOnMount={true} // optional, default true
>
{children}
</WattfareProvider> | Prop | Type | Default | Notes |
|---|---|---|---|
| publishableKey | string | — | Required. Starts with pk_. |
| session | string | () => string | Promise<string> | — | Required. Backend route path or a token-returning function. |
| baseURL | string | https://wattfare.com | Override the Wattfare service URL. |
| consentURL | string | https://wattfare.com | Override the consent popup origin. |
| loadOnMount | boolean | true | Fetch status once on mount. First-run “not connected” is swallowed, not surfaced as an error. |
useWattfare()
Must be called inside a <WattfareProvider> (it throws otherwise). Returns the
live connection snapshot plus the three actions.
import { useWattfare } from "wattfare/react";
function BudgetBar() {
const { connected, connect, disconnect, remainingUsd, loading } = useWattfare();
if (loading) return <Spinner />;
if (!connected) return <button onClick={connect}>⚡ Connect AI budget</button>;
return (
<div className="budget">
<span>{remainingUsd !== null ? `$${remainingUsd.toFixed(2)} left` : "Unlimited"}</span>
<button onClick={disconnect}>Disconnect</button>
</div>
);
} | Field | Type | Description |
|---|---|---|
| connected | boolean | Whether the user has an active budget connection. |
| remainingUsd | number | null | Remaining spend for the period. null = unlimited. |
| status | ConnectionStatus | The full snapshot (limits, usage, remainingUsd). |
| loading | boolean | True while any connect / disconnect / refresh is in flight. |
| error | Error | null | The last error, if any. |
| connect() | () => Promise<void> | Open the consent popup, then refresh status. Call from a click handler. |
| disconnect() | () => Promise<void> | Revoke the connection and reset to not-connected. |
| refresh() | () => Promise<void> | Re-fetch the connection + budget snapshot. |
| requestGrant(options) | (options: RequestGrantOptions) => Promise<GrantResult | null> | Open the consent popup for a one-time access grant. Returns the grant result or null if denied. Does not affect connection state. |
function Chat() {
const { connect, refresh, remainingUsd } = useWattfare();
async function send(prompt: string) {
await fetch("/api/chat", { method: "POST", body: JSON.stringify({ prompt }) });
await refresh(); // pull the new remaining budget after a call
}
// …
} One-time grants
requestGrant() opens the consent popup for a disposable access grant — it
doesn't affect connected or any other hook state. See
One-time grants for the full guide and examples.
SSR & “use client”
The provider and hook are client components — they use React context and effects. In Next.js App
Router, render <WattfareProvider> in a file marked "use client"
(or a client boundary) and keep your secret-key code in server route handlers. See the
Next.js guide for a full layout.