butr

Discover and connect EVM, Solana, Sui, Bitcoin, and Polkadot wallets from one React hook surface. Bring your own chain library.

$ npm i @usebutr/react @usebutr/wallets zustand
  • licenseMIT
  • walletsv2.0.1
  • packages12
  • react18+
  • Multi-chain

    Connect MetaMask and Phantom at the same time. One pool, each platform tracked on its own.

    See more
  • Connector-shaped

    Injected, WalletConnect, Ledger, or your own: every wallet is a WalletAdapter on one seam.

    See more
  • No lock-in

    getSigner() hands back the wallet's own provider, tagged by kind. Bridge it into viem, wagmi, gill, or @solana/kit.

    See more
  • Modular

    Core has no React and no protocol code. Install only the chain packages you need.

    See more

Overview

Wrap the tree once, read the discovered pool through hooks, and keep the picker UI yours. butr handles discovery and connection state across reloads; it ships no modal and no RPC stack.

// 1. Import the provider and hooks.
import {
  WalletManagerProvider,
  useConnect,
  useDiscoveredWallets,
  useIsHydrated,
} from "@usebutr/react";
import { autoDiscovery } from "@usebutr/wallets";

// 2. Discover the browser's wallets; config is read once.
const config = { sources: [autoDiscovery()] };

export const App = () => (
  <WalletManagerProvider config={config}>
    <WalletPicker />
  </WalletManagerProvider>
);

// 3. Read the pool; UI stays yours.
const WalletPicker = () => {
  const wallets = useDiscoveredWallets();
  const { connect } = useConnect();
  const isHydrated = useIsHydrated();
  // Wait for persisted connections before showing the picker.
  if (!isHydrated) return null;

  return wallets.map(({ id, name }) => (
    <button key={id} onClick={() => connect(id)} type="button">
      Connect {name}
    </button>
  ));
};

Supported chains

One discovery seam across EVM networks, Solana, Sui, Bitcoin, and Polkadot: EIP-6963, the Wallet Standard, injected fallbacks, and injectedWeb3.

  • Ethereum
  • Solana
  • Bitcoin
  • Base
  • Arbitrum
  • Optimism
  • Polygon
  • BNB Chain
  • Sui
  • Polkadot

Hand the signer to your library.

getSigner() hands back the wallet's own provider, tagged by kind. Wrap it with viem, wagmi, gill, or @solana/kit and keep the stack you already have.

import {
  useSelectedWallet,
  useSigner,
} from "@usebutr/react";
import {
  createWalletClient,
  custom,
  getAddress,
} from "viem";
import { sepolia } from "viem/chains";

export const useSelectedWalletClient = () => {
  const wallet = useSelectedWallet("evm");
  const { data: signer } = useSigner(wallet);
  if (!wallet || signer?.kind !== "eip1193") return null;

  return createWalletClient({
    account: getAddress(wallet.account.walletAddress),
    chain: sepolia,
    transport: custom(signer.provider),
  });
};

Get started in a few lines.

Install, quickstart, core concepts, and the full API reference are in the docs.