@ckb-ccc/connector-react is the recommended integration layer for React and Next.js applications. It provides a context Provider, a useCcc hook that exposes the full connector state, and a useSigner hook that returns the active signer whenever a wallet is connected.
Installation
npm install @ckb-ccc/connector-react
yarn add @ckb-ccc/connector-react
pnpm add @ckb-ccc/connector-react
import { ccc } from "@ckb-ccc/connector-react";
Provider
Wrap your application (or the subtree that needs wallet access) with ccc.Provider. It renders the wallet selection modal and makes the connector context available to all descendant components.
import { ccc } from "@ckb-ccc/connector-react";
export function App() {
return (
<ccc.Provider>
<YourApp />
</ccc.Provider>
);
}
Props
All props are optional except children.
| Prop | Type | Description |
|---|
children | ReactNode | The component subtree that can access the connector context. |
name | string | Display name shown in the connector UI for your application. |
icon | string | URL of your application icon shown in the connector UI. |
defaultClient | ccc.Client | Override the default CKB client. Defaults to ClientPublicTestnet. |
clientOptions | { icon?: string; client: ccc.Client; name: string }[] | List of named network clients users can switch between. |
preferredNetworks | ccc.NetworkPreference[] | Networks to prioritise when multiple signers are available. |
signerFilter | (signerInfo: ccc.SignerInfo, wallet: ccc.Wallet) => Promise<boolean> | Async predicate to hide specific signers from the UI. |
signersController | ccc.SignersController | Provide a custom signers controller, bypassing the built-in filter logic. |
hideMark | boolean | Hide the CCC watermark in the connector modal. |
connectorProps | HTMLAttributes<{}> | Additional HTML attributes forwarded to the connector web component element. |
useCcc
useCcc() returns the full connector context. Call it inside any component that is a descendant of ccc.Provider.
import { ccc } from "@ckb-ccc/connector-react";
function ConnectButton() {
const { open, disconnect, wallet, signerInfo, client } = ccc.useCcc();
if (signerInfo) {
return (
<div>
<p>Connected: {wallet?.name}</p>
<button onClick={disconnect}>Disconnect</button>
</div>
);
}
return <button onClick={open}>Connect wallet</button>;
}
Return value
| Property | Type | Description |
|---|
isOpen | boolean | Whether the connector modal is currently visible. |
open | () => void | Open the wallet selection modal. |
close | () => void | Close the wallet selection modal. |
disconnect | () => void | Disconnect the currently connected wallet. |
setClient | (client: ccc.Client) => void | Switch to a different CKB client at runtime. |
client | ccc.Client | The active CKB client instance. |
wallet | ccc.Wallet | undefined | The connected wallet metadata, or undefined when no wallet is connected. |
signerInfo | ccc.SignerInfo | undefined | The active signer info (contains .signer), or undefined when disconnected. |
Calling useCcc() outside of a ccc.Provider subtree throws an error. Make sure every component that uses this hook is wrapped by the provider.
useSigner
useSigner() is a convenience hook that returns the active ccc.Signer directly. It returns undefined while no wallet is connected.
import { ccc } from "@ckb-ccc/connector-react";
function SendButton() {
const signer = ccc.useSigner();
async function sendCkb() {
if (!signer) return;
const { script: toLock } = await ccc.Address.fromString(
"ckb1...",
signer.client,
);
const tx = ccc.Transaction.from({
outputs: [{ lock: toLock, capacity: ccc.fixedPointFrom("100") }],
});
await tx.completeInputsByCapacity(signer);
await tx.completeFeeBy(signer);
const txHash = await signer.sendTransaction(tx);
console.log("Sent:", txHash);
}
return (
<button onClick={sendCkb} disabled={!signer}>
Send 100 CKB
</button>
);
}
Full example
The following example shows a minimal React app that connects a wallet and sends CKB.
"use client"; // Required for Next.js App Router
import { ccc } from "@ckb-ccc/connector-react";
function WalletControls() {
const { open, disconnect, wallet, signerInfo } = ccc.useCcc();
const signer = ccc.useSigner();
async function handleSend() {
if (!signer) return;
const { script: toLock } = await ccc.Address.fromString(
"ckb1qzda0cr08m85hc8jlnfp3gp88t7re8he4qkd3he97k2tfe4ckb...",
signer.client,
);
const tx = ccc.Transaction.from({
outputs: [{ lock: toLock, capacity: ccc.fixedPointFrom("100") }],
});
await tx.completeInputsByCapacity(signer);
await tx.completeFeeBy(signer);
const txHash = await signer.sendTransaction(tx);
alert(`Sent! TX: ${txHash}`);
}
return (
<div>
{signerInfo ? (
<>
<p>Connected via {wallet?.name}</p>
<button onClick={handleSend}>Send 100 CKB</button>
<button onClick={disconnect}>Disconnect</button>
</>
) : (
<button onClick={open}>Connect wallet</button>
)}
</div>
);
}
export default function App() {
return (
<ccc.Provider>
<WalletControls />
</ccc.Provider>
);
}
In Next.js App Router, add "use client" to any file that imports from @ckb-ccc/connector-react. The Provider and hooks use React context and browser APIs that are not available in server components.