butr
Get started

Quickstart

A working multi-chain wallet UI in five minutes with WalletManagerProvider.

This walks through the smallest useful app: discover wallets, connect one, show it, disconnect. It mirrors the demo-vite reference app, which uses the batteries-included @usebutr/wallets package.

1. Wrap your app in the provider

WalletManagerProvider from @usebutr/react is the single provider. Pass it a WalletSource via the discovery prop to enable auto-discovery. autoDiscovery() from @usebutr/wallets composes EVM (EIP-6963) and Solana (Wallet Standard) in one call. storageKeyPrefix namespaces the storage keys.

Hoist the discovery source to module scope so the same instance is reused across renders:

wallet-provider.tsx
import type { ReactNode } from "react";
import { WalletManagerProvider } from "@usebutr/react";
import { autoDiscovery } from "@usebutr/wallets";

const discovery = autoDiscovery();

const WalletProvider = ({ children }: { children: ReactNode }) => (
  <WalletManagerProvider discovery={discovery} storageKeyPrefix="butr-demo">
    {children}
  </WalletManagerProvider>
);

export { WalletProvider };

Mount it above your tree:

main.tsx
import ReactDOM from "react-dom/client";
import { App } from "./app";
import { WalletProvider } from "./wallet-provider";

ReactDOM.createRoot(document.querySelector("#root")!).render(
  <WalletProvider>
    <App />
  </WalletProvider>,
);

2. Wait for hydration

butr restores the previous session asynchronously. Gate your UI on useIsHydrated() so you don't flash a "not connected" state on reload.

import { useIsHydrated } from "@usebutr/react";

const Content = () => {
  const isHydrated = useIsHydrated();
  if (!isHydrated) return <p>Loading…</p>;
  // …
};

3. List discovered wallets and connect

useDiscoveredWallets() is the live list of adapters that have announced themselves. useConnectWallet() returns a function you call with an adapter id.

import { useConnectWallet, useConnectedWallets, useDiscoveredWallets } from "@usebutr/react";

const WalletPicker = () => {
  const connect = useConnectWallet();
  const discovered = useDiscoveredWallets();
  const connected = useConnectedWallets();

  const available = discovered.filter((d) => !connected.some((c) => c.connector.id === d.id));

  return (
    <ul>
      {available.map((wallet) => (
        <li key={wallet.id}>
          <button type="button" onClick={() => connect(wallet.id)}>
            Connect {wallet.name} ({wallet.chainPlatform})
          </button>
        </li>
      ))}
    </ul>
  );
};

4. Show connected wallets and disconnect

import { useConnectedWallets, useDisconnectWallet } from "@usebutr/react";

const Connected = () => {
  const wallets = useConnectedWallets();
  const disconnect = useDisconnectWallet();

  return (
    <ul>
      {wallets.map((wallet) => (
        <li key={wallet.connector.id}>
          {wallet.connector.name} — {wallet.account.walletAddress}
          <button type="button" onClick={() => disconnect(wallet.connector.id)}>
            Disconnect
          </button>
        </li>
      ))}
    </ul>
  );
};

That is a complete multi-chain connect flow. The same wallet.connector exposes signMessage, sendTx, switchChain, and getSigner — see the guides.

Source: apps/demo-vite in the butr repository. Run it locally with pnpm dev --filter=demo-vite (serves on http://localhost:5173).

Next steps

On this page