connectorkit

star 65

Solana wallet connection library with headless core and React UI components. Use when working with @solana/connector, ConnectorKit, Solana wallet connection, wallet standard integration, ConnectorClient, wallet elements, render props, useWallet, useConnectWallet, useBalance, useTransactionSigner, WalletListElement, AccountElement, BalanceElement, TransactionHistoryElement, migrating from @solana/wallet-adapter, remote signer, server-side signing, Fireblocks Solana, Privy Solana, or mobile wallet adapter integration.

solana-foundation By solana-foundation schedule Updated 3/15/2026

name: connectorkit description: > Solana wallet connection library with headless core and React UI components. Use when working with @solana/connector, ConnectorKit, Solana wallet connection, wallet standard integration, ConnectorClient, wallet elements, render props, useWallet, useConnectWallet, useBalance, useTransactionSigner, WalletListElement, AccountElement, BalanceElement, TransactionHistoryElement, migrating from @solana/wallet-adapter, remote signer, server-side signing, Fireblocks Solana, Privy Solana, or mobile wallet adapter integration.


ConnectorKit

@solana/connector — Headless wallet connection library for Solana.

Entry Points

Import Purpose
@solana/connector Full library (React + headless)
@solana/connector/headless Framework-agnostic core (Vue, Svelte, vanilla JS)
@solana/connector/react React hooks + element components
@solana/connector/compat Bridge for existing @solana/wallet-adapter code
@solana/connector/remote Browser-side remote wallet adapter
@solana/connector/server Server-side route handlers for remote signing

React hooks and elements are only available via @solana/connector or @solana/connector/react. For non-React frameworks, use ConnectorClient from @solana/connector/headless and subscribe to state changes directly.

Recommended Imports

  • Prefer explicit entry points for clarity:
    • React: @solana/connector/react
    • Headless/core: @solana/connector/headless
  • Use @solana/connector as a convenience re-export (it re-exports both ./react and ./headless).

Next.js (App Router) Setup

Put providers in a single client boundary (typically app/providers.tsx) and keep the rest of your app as Server Components.

// app/providers.tsx
'use client';

import type { ReactNode } from 'react';
import { useMemo } from 'react';
import { AppProvider } from '@solana/connector/react';
import { getDefaultConfig, getDefaultMobileConfig } from '@solana/connector/headless';

function getOrigin() {
    if (typeof window === 'undefined') return 'http://localhost:3000';
    return window.location.origin;
}

export function Providers({ children }: { children: ReactNode }) {
    const connectorConfig = useMemo(
        () =>
            getDefaultConfig({
                appName: 'My App',
                appUrl: getOrigin(),
                network: 'devnet',
                autoConnect: true,
                enableMobile: true,
                walletConnect: true, // reads NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID
            }),
        [],
    );

    const mobile = useMemo(
        () =>
            getDefaultMobileConfig({
                appName: 'My App',
                appUrl: getOrigin(),
            }),
        [],
    );

    return (
        <AppProvider connectorConfig={connectorConfig} mobile={mobile}>
            {children}
        </AppProvider>
    );
}
// app/layout.tsx
import type { ReactNode } from 'react';
import { Providers } from './providers';

export default function RootLayout({ children }: { children: ReactNode }) {
    return (
        <html lang="en">
            <body>
                <Providers>{children}</Providers>
            </body>
        </html>
    );
}

Quick Start (React)

import { AppProvider } from '@solana/connector/react';
import { getDefaultConfig } from '@solana/connector/headless';

const config = getDefaultConfig({
    appName: 'My App',
    network: 'devnet',
    autoConnect: true,
    enableMobile: true,
    walletConnect: true, // optional; reads NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID
});

function App() {
    return (
        <AppProvider connectorConfig={config}>
            <WalletUI />
        </AppProvider>
    );
}

Common Patterns

Connect a Wallet

import { useConnectWallet, useWalletConnectors, useWallet } from '@solana/connector/react';

function ConnectButton() {
    const { status, account } = useWallet();
    const { connect, isConnecting } = useConnectWallet();
    const connectors = useWalletConnectors();

    if (status === 'connected') return <div>{account}</div>;

    return connectors.map(w => (
        <button key={w.id} onClick={() => connect(w.id)} disabled={isConnecting}>
            {w.name}
        </button>
    ));
}

Sign & Send a Transaction

import { useTransactionSigner } from '@solana/connector/react';

const { signer, ready } = useTransactionSigner();
const sig = await signer.signAndSendTransaction(transaction);

For @solana/kit compatible signer: useKitTransactionSigner().

Elements with Render Props

All elements accept a render prop for full UI customization:

import { WalletListElement, AccountElement, BalanceElement } from '@solana/connector/react'

// Default rendering
<WalletListElement />
<AccountElement showAvatar showCopy />
<BalanceElement showTokens />

// Custom via render prop
<AccountElement render={({ address, formatted, copy, copied }) => (
  <div onClick={copy}>{copied ? 'Copied!' : formatted}</div>
)} />

<WalletListElement render={({ wallets, connectById, connecting }) => (
  wallets.map(w => (
    <button key={w.id} onClick={() => connectById(w.id)}>{w.name}</button>
  ))
)} />

All elements: WalletListElement, AccountElement, ClusterElement, DisconnectElement, BalanceElement, TransactionHistoryElement, TokenListElement, SkeletonShine

Switch Networks

import { useCluster } from '@solana/connector/react';

const { cluster, clusters, setCluster, isMainnet, isDevnet } = useCluster();
await setCluster('devnet');

Wallet Filtering & Ordering

Improve UX for users with lots of installed wallets by filtering and/or featuring wallets at the config level:

import { getDefaultConfig } from '@solana/connector/headless';

const config = getDefaultConfig({
    appName: 'My App',
    wallets: {
        allowList: ['Phantom', 'Solflare', 'Backpack'],
        denyList: ['MetaMask'],
        featured: ['Phantom', 'Solflare'],
    },
});

WalletConnect (QR / Deep Link)

  • Install the WalletConnect peer dependency: npm i @walletconnect/universal-provider
  • Set NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=...
  • Enable in config: walletConnect: true

When a pairing URI is available, render a QR/deep link UI (e.g. a dialog). The provider exposes walletConnectUri and clearWalletConnectUri.

'use client';

import { useConnector } from '@solana/connector/react';

export function WalletConnectQrModal() {
    const { walletConnectUri, clearWalletConnectUri } = useConnector();
    if (!walletConnectUri) return null;

    return (
        <div>
            <div>WalletConnect URI: {walletConnectUri}</div>
            <button onClick={clearWalletConnectUri}>Close</button>
        </div>
    );
}

For a full QR example, see packages/connector/README.md (WalletConnect section).

Remote Signer (Server-backed Signing)

Remote signing is a two-part setup:

  1. Browser: create a Wallet Standard wallet that delegates signing to your API.
import { createRemoteSignerWallet } from '@solana/connector/remote';
import { getDefaultConfig } from '@solana/connector/headless';

const remoteWallet = createRemoteSignerWallet({
    endpoint: '/api/connector-signer',
    name: 'Treasury',
    // getAuthHeaders: () => ({ Authorization: `Bearer ${token}` }),
});

const config = getDefaultConfig({
    appName: 'My App',
    additionalWallets: [remoteWallet],
});
  1. Server: implement the Next.js route handler to actually sign.
// app/api/connector-signer/route.ts
import { createRemoteSignerRouteHandlers } from '@solana/connector/server';

const { GET, POST } = createRemoteSignerRouteHandlers({
    provider: { type: 'custom', signer: myRemoteSigner }, // implements RemoteSigner (see references/remote-signer.md)
    authorize: async request => true,
    policy: {
        validateTransaction: async bytes => true,
        validateMessage: async bytes => true,
    },
    rpc: { endpoint: process.env.RPC_URL },
    chains: ['solana:mainnet'],
    name: 'Treasury',
});

export { GET, POST };

Details (provider configs + protocol types + security) live in references/remote-signer.md.

Devtools (Development Only)

ConnectorKit also ships devtools (@solana/connector-debugger) that can be dynamically mounted in development.

'use client';

import { useEffect } from 'react';

export function DevtoolsLoader() {
    useEffect(() => {
        if (process.env.NODE_ENV !== 'development') return;

        let devtools: { mount: (el: HTMLElement) => void; unmount: () => void } | undefined;
        let container: HTMLDivElement | undefined;

        import('@solana/connector-debugger').then(({ ConnectorDevtools }) => {
            container = document.createElement('div');
            document.body.appendChild(container);
            devtools = new ConnectorDevtools({ config: { position: 'bottom-right', theme: 'dark' } });
            devtools.mount(container);
        });

        return () => {
            devtools?.unmount();
            container?.remove();
        };
    }, []);

    return null;
}

Security & Production Checklist

  • RPC API keys: don’t expose paid RPC URLs as NEXT_PUBLIC_*; use a Next.js RPC proxy route and point your cluster URL at the proxy (see packages/connector/README.md).
  • Token logo privacy: set imageProxy if you render token images to avoid leaking user IPs to arbitrary token metadata hosts (see packages/connector/README.md).
  • CoinGecko rate limits: if you use token prices heavily, configure coingecko.apiKey.
  • Remote signer: always implement authorize + policy.validateTransaction, use HTTPS, and rate limit the signer endpoint.

Headless (Non-React)

import { ConnectorClient } from '@solana/connector/headless';

const client = new ConnectorClient({
    autoConnect: true,
    cluster: { initialCluster: 'devnet' },
});

client.subscribe(state => {
    /* react to changes */
});
await client.connectWallet('wallet-standard:phantom');
const snapshot = client.getSnapshot();

Wallet Adapter Compat Bridge

import { useWalletAdapterCompat } from '@solana/connector/compat';

const compat = useWalletAdapterCompat(signer, disconnect);
compat.publicKey; // string | null
compat.connected; // boolean
compat.signTransaction(tx);
compat.sendTransaction(tx, connection);

Key Concepts

Connector IDs

Wallets use stable branded WalletConnectorId strings:

  • 'wallet-standard:phantom', 'wallet-standard:solflare', etc.
  • 'walletconnect'
  • 'mwa:phantom' (Mobile Wallet Adapter on iOS/Android)

Wallet Status State Machine

useWallet() returns a discriminated union:

status: 'disconnected' | 'connecting' | 'connected' | 'error';

When status === 'connected', session contains accounts, selectedAccount, selectAccount().

Type guards: isDisconnected(), isConnecting(), isConnected(), isStatusError()

Providers

  • <AppProvider> — Convenience wrapper (recommended). Auto-wires ConnectorProvider, WalletConnect, error boundaries.
  • <ConnectorProvider> — Lower-level, accepts config and mobile props.
  • <ConnectorErrorBoundary> — Catches errors in connector tree.

Reference Files

Install via CLI
npx skills add https://github.com/solana-foundation/connectorkit --skill connectorkit
Repository Details
star Stars 65
call_split Forks 14
navigation Branch main
article Path SKILL.md
More from Creator
solana-foundation
solana-foundation Explore all skills →