React Hooks
PartyLayer provides React hooks for accessing wallet state, performing operations, and managing sessions. The hooks below are the main entrypoint (@partylayer/react): they use useSyncExternalStore and must be used within a PartyLayerKit or PartyLayerProvider. v2 also adds a TanStack Query powered @partylayer/react/query entrypoint for ledger data and cost, documented in Data hooks at the end of this page.
Core Hooks
usePartyLayer
Access the underlying PartyLayerClient instance directly.
import { usePartyLayer } from '@partylayer/react';
function Advanced() {
const client = usePartyLayer();
// Access any client method
const wallets = await client.listWallets();
const provider = client.asProvider();
}
ℹ️ Note
Use this hook when you need direct access to the SDK client for operations not covered by the other hooks (e.g.,
asProvider(),
registerAdapter()).
useSession
Reactive session state and actions. Re-renders on every session change. This returns UseSessionReturn (the reactive store), not the legacy SDK session getter.
import { useSession } from '@partylayer/react';
function Profile() {
const { status, account, networkId, isConnected, disconnect } = useSession();
if (!isConnected) return <p>Not connected ({status})</p>;
return (
<div>
<p>Party ID: {account?.partyId}</p>
<p>Network: {networkId}</p>
<button onClick={() => disconnect()}>Disconnect</button>
</div>
);
}
Return type: UseSessionReturn, the reactive SessionState (status, account, accounts, networkId, lastError) plus isConnected/isConnecting/isReconnecting/isDisconnected and the actions connect, disconnect, restore, on.
⚠️ Warning
Migration: useSession() was re-pointed from the SDK-layer session getter (
Session | null) to the reactive store. The old getter is preserved VERBATIM as
useClientSession() (deprecated): it still returns the
Session object (
sessionId,
walletId,
partyId,
network, …). Migrate
useSession() →
useClientSession() if you need that shape.
useWallets
Fetch and list all available wallets (from both the registry and registered adapters).
import { useWallets } from '@partylayer/react';
function WalletList() {
const { wallets, isLoading, error } = useWallets();
if (isLoading) return <p>Loading wallets...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{wallets.map(w => (
<li key={w.walletId}>
{w.name}: {w.capabilities.join(', ')}
</li>
))}
</ul>
);
}
Return type: { wallets: WalletInfo[], isLoading: boolean, error: Error | null }
Connection Hooks
useConnect
Connect to a wallet programmatically.
import { useConnect } from '@partylayer/react';
function CustomConnect() {
const { connect, isConnecting, error, reset } = useConnect();
const handleConnect = async () => {
const session = await connect({ walletId: 'console' });
if (session) {
console.log('Connected:', session.partyId);
}
};
return (
<div>
<button onClick={handleConnect} disabled={isConnecting}>
{isConnecting ? 'Connecting...' : 'Connect Console'}
</button>
{error && (
<div>
<p>Error: {error.message}</p>
<button onClick={reset}>Reset</button>
</div>
)}
</div>
);
}
Return type: { connect: (options?) => Promise<Session | null>, isConnecting: boolean, error: Error | null, reset: () => void }
The options parameter accepts: walletId (optional: if omitted, opens the modal).
useDisconnect
Disconnect the active wallet session.
import { useDisconnect } from '@partylayer/react';
function DisconnectButton() {
const { disconnect, isDisconnecting, error } = useDisconnect();
return (
<button onClick={() => disconnect()} disabled={isDisconnecting}>
{isDisconnecting ? 'Disconnecting...' : 'Disconnect'}
</button>
);
}
Return type: { disconnect: () => Promise<void>, isDisconnecting: boolean, error: Error | null }
Signing Hooks
useSignMessage
Sign an arbitrary message with the connected wallet.
import { useSignMessage } from '@partylayer/react';
function SignDemo() {
const { signMessage, isSigning, error } = useSignMessage();
const handleSign = async () => {
const result = await signMessage({
message: 'Hello from PartyLayer!',
nonce: crypto.randomUUID(),
});
if (result) {
console.log('Signature:', result.signature);
console.log('Signed by:', result.partyId);
}
};
return (
<button onClick={handleSign} disabled={isSigning}>
{isSigning ? 'Signing...' : 'Sign Message'}
</button>
);
}
Return type: { signMessage: (params) => Promise<SignedMessage | null>, isSigning: boolean, error: Error | null }
SignedMessage includes: signature, partyId, message, nonce, domain.
useSignTransaction
Sign a transaction without submitting it.
import { useSignTransaction } from '@partylayer/react';
function SignTx() {
const { signTransaction, isSigning, error } = useSignTransaction();
const handleSign = async () => {
const result = await signTransaction({
tx: { templateId: '...', choiceId: '...', argument: { /* ... */ } },
});
if (result) {
console.log('Transaction hash:', result.transactionHash);
console.log('Signed payload:', result.signedTx);
}
};
return <button onClick={handleSign}>{isSigning ? 'Signing...' : 'Sign Transaction'}</button>;
}
Return type: { signTransaction: (params) => Promise<SignedTransaction | null>, isSigning: boolean, error: Error | null }
useSubmitTransaction
Sign and submit a transaction to the ledger in one step.
import { useSubmitTransaction } from '@partylayer/react';
function SubmitTx() {
const { submitTransaction, isSubmitting, error } = useSubmitTransaction();
const handleSubmit = async () => {
const receipt = await submitTransaction({
signedTx: signedPayload, // Pass the signed transaction
});
if (receipt) {
console.log('TX Hash:', receipt.transactionHash);
console.log('Submitted at:', new Date(receipt.submittedAt));
console.log('Command ID:', receipt.commandId);
}
};
return <button onClick={handleSubmit}>{isSubmitting ? 'Submitting...' : 'Submit'}</button>;
}
Return type: { submitTransaction: (params) => Promise<TxReceipt | null>, isSubmitting: boolean, error: Error | null }
useLedgerApi
Call the Canton Ledger API through the connected wallet.
import { useLedgerApi, useAccount } from '@partylayer/react';
function BalanceQuery() {
const { isConnected, party } = useAccount();
const { ledgerApi, isLoading, error } = useLedgerApi();
const fetchBalance = async () => {
if (!isConnected || !party) return;
const result = await ledgerApi({
requestMethod: 'POST',
resource: '/v2/state/active-contracts',
body: JSON.stringify({
filter: {
filtersByParty: {
[party]: {
inclusive: {
templateFilters: [{ templateId: 'Splice.Amulet:Amulet' }],
},
},
},
},
}),
});
if (result) {
const { activeContracts = [] } = JSON.parse(result.response);
console.log('Contracts:', activeContracts.length);
}
};
return (
<button onClick={fetchBalance} disabled={isLoading}>
{isLoading ? 'Loading...' : 'Fetch Balance'}
</button>
);
}
Return type: { ledgerApi: (params) => Promise<LedgerApiResult | null>, isLoading: boolean, error: Error | null }
Requires a wallet with ledgerApi capability, see Capability Matrix. Throws CapabilityNotSupportedError for wallets that don't support it (e.g. Cantor8).
Utility Hooks
useRegistryStatus
Get the current wallet registry status and refresh it.
import { useRegistryStatus } from '@partylayer/react';
function RegistryInfo() {
const { status, refresh } = useRegistryStatus();
if (!status) return <p>No registry data</p>;
return (
<div>
<p>Source: {status.source}</p>
<p>Verified: {status.verified ? 'Yes' : 'No'}</p>
<p>Channel: {status.channel}</p>
<p>Stale: {status.stale ? 'Yes' : 'No'}</p>
<button onClick={refresh}>Refresh</button>
</div>
);
}
Return type: { status: RegistryStatus | null, refresh: () => Promise<void> }
useWalletIcons
Access the wallet icon overrides provided by PartyLayerKit.
import { useWalletIcons, resolveWalletIcon } from '@partylayer/react';
function WalletIcon({ walletId, registryIcon }: { walletId: string; registryIcon?: string }) {
const walletIcons = useWalletIcons();
const iconUrl = resolveWalletIcon(walletId, walletIcons, registryIcon);
if (!iconUrl) return <div className="fallback-icon" />;
return <img src={iconUrl} alt={walletId} width={32} height={32} />;
}
Return type: Record<string, string>
useTheme
Access the current PartyLayer theme.
import { useTheme } from '@partylayer/react';
function ThemedComponent() {
const theme = useTheme();
return (
<div style={{
background: theme.colors.background,
color: theme.colors.text,
fontFamily: theme.fontFamily,
}}>
Current mode: {theme.mode}
</div>
);
}
Return type: PartyLayerTheme, see Theming for the full interface.
Data hooks (@partylayer/react/query)
v2 adds a TanStack Query powered entrypoint for reading and writing ledger data and for cost estimation. These hooks import from @partylayer/react/query and require a QueryClientProvider (see Quick Start). PartyLayer does not own ledger transport: you supply the fetcher, and the hook wraps it in useQuery / useMutation.
ℹ️ Requires QueryClientProvider
The hooks above (the main entrypoint) work without TanStack Query. The data hooks here need a
QueryClient in context, so wrap your app in
QueryClientProvider (in addition to
PartyLayerKit /
PartyLayerProvider). The entrypoint also exports query-backed variants of
useConnect,
useWallets,
useDisconnect,
useSignMessage, and
useSubmitTransaction.
useDamlContract / useChoice (DAML, Model 2)
Read a contract (generic over its type) and exercise a choice. You supply the fetcher.
import { useDamlContract, useChoice } from '@partylayer/react/query';
// read: you supply the `read` fetcher; null is a valid (absent) value, not an error.
const { contract, isLoading } = useDamlContract<MyContract>({ read: fetchContract });
// write: exposes exerciseChoice / exerciseChoiceAsync.
const { exerciseChoice, exerciseChoiceAsync } = useChoice<MyResult, MyVars>({ exercise });
useTransactionCostEstimate / usePaidTrafficCost (CIP-0104)
import { useTransactionCostEstimate, usePaidTrafficCost } from '@partylayer/react/query';
const { costEstimate } = useTransactionCostEstimate({ estimate: fetchEstimate });
const { paidTrafficCost } = usePaidTrafficCost({ fetch: fetchPaid });
useTransactionCostEstimate is the pre-submission CostEstimation; usePaidTrafficCost is the post-execution paid cost.
Suspense twins
The query hooks have useSuspense* twins (useSuspenseTransactionCostEstimate, useSuspensePaidTrafficCost, useSuspenseWallets) for declarative loading inside a React <Suspense> boundary: the value is always present (no loading flag), and the boundary shows the fallback while it resolves.
Optimistic updates
optimisticMutationOptions wires an optimistic cache update with automatic rollback on error into useChoice. The partyLayerKeys factory (also exported here) produces the hierarchical cache keys, so manual cache reads/writes line up with the hooks. See the Pattern Cookbook for full recipes.