Get Started

Installation

Install CCC for your environment — React frontend, Node.js backend, or custom UI.

Edit on GitHub

Not sure where to start? The Quick start guide walks through a full React setup from scratch. Come back here when you need a specific package or TypeScript configuration details.

CCC ships as several focused packages. Pick the one that matches your environment:

PackageUse case
@ckb-ccc/connector-reactReact applications
@ckb-ccc/shellNode.js scripts and backends
@ckb-ccc/connectorWeb Component (framework-agnostic)
@ckb-ccc/cccCustom UI, manual signer management
npm install @ckb-ccc/connector-react
yarn add @ckb-ccc/connector-react
pnpm add @ckb-ccc/connector-react

Wrap your app root with ccc.Provider to mount the wallet connector UI and make the CCC context available everywhere:

Using Next.js App Router or another RSC setup? Add "use client" at the top of any file that imports ccc.Provider or ccc.useCcc.

app.tsx
"use client";

import { ccc } from "@ckb-ccc/connector-react";

export default function App({ children }: { children: React.ReactNode }) {
  return (
    <ccc.Provider name="My CKB App" icon="/icon.png">
      {children}
    </ccc.Provider>
  );
}

Then call ccc.useCcc() from any child component to open the connector and access the active signer:

component.tsx
"use client";

import { ccc } from "@ckb-ccc/connector-react";

export function ConnectButton() {
  const { open, wallet, signerInfo } = ccc.useCcc();

  return signerInfo ? (
    <p>Connected: {wallet?.name}</p>
  ) : (
    <button onClick={open}>Connect wallet</button>
  );
}
npm install @ckb-ccc/shell
yarn add @ckb-ccc/shell
pnpm add @ckb-ccc/shell

@ckb-ccc/shell bundles everything from @ckb-ccc/core — addresses, bytes, clients, transactions, signers — plus domain-specific modules for working with Spore digital objects, SSRI contracts, and UDT tokens. It's the right choice for scripts, backends, and CLI tools.

script.ts
import { ccc } from "@ckb-ccc/shell";

const client = new ccc.ClientPublicTestnet();
const signer = new ccc.SignerCkbPrivateKey(client, process.env.PRIVATE_KEY!);

const address = await signer.getRecommendedAddress();
console.log("Address:", address);
npm install @ckb-ccc/connector
yarn add @ckb-ccc/connector
pnpm add @ckb-ccc/connector

Importing the package registers the <ccc-connector> custom element automatically — no extra setup needed:

main.ts
import { ccc } from "@ckb-ccc/connector";
index.html
<ccc-connector></ccc-connector>
npm install @ckb-ccc/ccc
yarn add @ckb-ccc/ccc
pnpm add @ckb-ccc/ccc

Use @ckb-ccc/ccc when you want full control over the wallet connection UI. It includes all wallet integrations — EIP-6963 (EVM), JoyID, NIP-07 (Nostr), OKX, UniSat, UTXO Global, Xverse, and more — without any pre-built UI:

custom-ui.ts
import { ccc } from "@ckb-ccc/ccc";

const client = new ccc.ClientPublicTestnet();
const controller = new ccc.SignersController();
let wallets: ccc.WalletWithSigners[] | undefined;

// Fetch all available signers
await controller.refresh(client, (w) => (wallets = w));
if (!wallets) {
  throw new Error("Unexpected not wallets");
}
wallets.forEach((wallet) => {
  console.log(
    wallet.name,
    wallet.signers.map(({ name }) => name),
  );
});

const signer = wallets[0].signers[0].signer;

// Connect signer
await signer.connect();
console.log("Connected");

// Sign message as test
const signature = await signer.signMessage("Hello world");
console.log(signature);

For a detailed walkthrough, see the Connect wallets guide.

Import patterns

Every CCC package exposes the same ccc namespace, so your import looks identical regardless of which package you're using:

import { ccc } from "@ckb-ccc/<package-name>";

const tx = ccc.Transaction.from({ ... });
const amount = ccc.fixedPointFrom("100");

For lower-level internals, the /advanced entry point exports cccA:

import { cccA } from "@ckb-ccc/<package-name>/advanced";

cccA APIs are unstable and may change between versions without notice. Only reach for them when ccc doesn't cover your use case.

TypeScript configuration

CCC uses Package Entry Points for tree-shaking. Set moduleResolution to node16, nodenext, or bundler in your tsconfig.json, and don't disable resolvePackageJsonExports:

tsconfig.json
{
  "compilerOptions": {
    "moduleResolution": "bundler"
  }
}

If you see Property '*' does not exist on type 'typeof import(".../@ckb-ccc/...")', your moduleResolution is likely misconfigured. See the TypeScript module resolution docs for details.

On this page