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:
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:
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
- Core concepts — what the pool, selection, and active wallet mean, and why hydration is asynchronous.
- Multi-chain & switching — connect more than one wallet and switch chains.
- Signing and Transactions.