Skip to main content
CCC ships a ready-made React connector that surfaces a wallet picker UI supporting wallets from EVM, BTC, Nostr, and native CKB ecosystems — all through a single <ccc.Provider> wrapper.

Supported wallets

MetaMask

EVM-based signing via MetaMask and other EVM-compatible wallets.

JoyID

Passkey-based CKB wallet — no seed phrase required.

UniSat

BTC wallet with CKB support via the UniSat extension.

OKX

OKX wallet supporting BTC, EVM, and CKB signers.

UTXO Global

UTXO-based multi-chain wallet with CKB integration.

Nostr

Sign with a Nostr key pair for decentralized identity.

Steps

1

Install the package

Add @ckb-ccc/connector-react to your project.
npm install @ckb-ccc/connector-react
2

Wrap your app in the Provider

Import ccc from the connector package and wrap your application root with <ccc.Provider>. The Provider injects the connector UI and context that all child components can access.
app.tsx
import { ccc } from "@ckb-ccc/connector-react";

export function App() {
  return (
    <ccc.Provider>
      <YourApp />
    </ccc.Provider>
  );
}
If you are using Next.js with the App Router, add "use client" at the top of any file that renders <ccc.Provider> or uses the CCC hooks, because they rely on React context and browser APIs.

Provider props

PropTypeDescription
namestringApp name shown in the wallet picker.
iconstringApp icon URL shown in the wallet picker.
defaultClientccc.ClientOverride the default CKB client (defaults to testnet).
clientOptions{ name, icon, client }[]List of network options for the user to switch between.
preferredNetworksccc.NetworkPreference[]Networks to prefer for each signer type; the connector prompts the wallet to switch if it is on a different network.
signerFilter(signerInfo, wallet) => Promise<boolean>Filter which signers appear in the picker.
app.tsx
import { ccc } from "@ckb-ccc/connector-react";

const mainnetClient = new ccc.ClientPublicMainnet();
const testnetClient = new ccc.ClientPublicTestnet();

export function App() {
  return (
    <ccc.Provider
      name="My CKB App"
      icon="https://example.com/icon.png"
      defaultClient={testnetClient}
      clientOptions={[
        { name: "Mainnet", client: mainnetClient },
        { name: "Testnet", client: testnetClient },
      ]}
    >
      <YourApp />
    </ccc.Provider>
  );
}
3

Read wallet state with useCcc()

Call useCcc() inside any component that is a descendant of <ccc.Provider>. It returns the current connection state and actions.
wallet-button.tsx
import { ccc } from "@ckb-ccc/connector-react";

export function WalletButton() {
  const { open, disconnect, wallet, signerInfo } = 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 values

ValueTypeDescription
open() => voidOpens the wallet picker modal.
close() => voidCloses the wallet picker modal.
disconnect() => voidDisconnects the current wallet.
walletccc.Wallet | undefinedThe currently connected wallet (name and icon).
signerInfoccc.SignerInfo | undefinedThe active signer with name and signer instance.
clientccc.ClientThe active CKB client.
isOpenbooleanWhether the wallet picker modal is open.
setClient(client: ccc.Client) => voidSwitch the active client programmatically.
useCcc() throws if the component is rendered outside of <ccc.Provider>. Make sure the Provider wraps your entire component tree before using the hook.
4

Get the signer with useSigner()

Use the useSigner() shorthand when you only need the Signer object to build and send transactions.
transfer-button.tsx
import { ccc } from "@ckb-ccc/connector-react";

export function TransferButton() {
  const signer = ccc.useSigner();

  async function handleSend() {
    if (!signer) return;
    const address = await signer.getRecommendedAddress();
    console.log("My address:", address);
  }

  return (
    <button onClick={handleSend} disabled={!signer}>
      Get Address
    </button>
  );
}
useSigner() returns undefined when no wallet is connected, so always check before using it.

Filter signers

Use signerFilter to restrict which wallets appear in the picker. The function receives a ccc.SignerInfo and the parent ccc.Wallet, and must return a promise that resolves to true to include the signer or false to hide it.
app.tsx
import { ccc } from "@ckb-ccc/connector-react";

export function App() {
  return (
    <ccc.Provider
      signerFilter={async (signerInfo, wallet) => {
        // Show only CKB-type signers
        return signerInfo.signer.type === ccc.SignerType.CKB;
      }}
    >
      <YourApp />
    </ccc.Provider>
  );
}

Next steps

Send CKB

Build and broadcast a CKB transfer with the signer you just connected.

Sign Message

Request a signed message from the connected wallet.