@usebutr/react
v2.0.1
Published
React provider and hooks for butr. Depends on @usebutr/core, react, zustand.
Maintainers
Readme
@usebutr/react
React provider and hooks for butr. Depends on @usebutr/core, react, zustand.
Part of butr, a multi-chain wallet discovery and connection-state library. Your application owns the picker UI and chain client.
Install
npm install @usebutr/react @usebutr/wallets zustandReact 19+ is a peer dependency. Run this inside an existing React app; the picker UI belongs to your application.
Usage
import { WalletManagerProvider, useConnect, useDiscoveredWallets, useWallet } from "@usebutr/react";
import { autoDiscovery } from "@usebutr/wallets";
// Read once at mount: define it at module scope.
const config = { sources: [autoDiscovery()] };
const WalletPicker = () => {
const wallets = useDiscoveredWallets();
const wallet = useWallet();
const { connect, error, status } = useConnect();
if (wallet) {
return <p>{wallet.account.walletAddress}</p>;
}
return (
<>
{wallets.map(({ id, name }) => (
<button
disabled={status === "connecting"}
key={id}
onClick={() => connect(id)}
type="button"
>
Connect {name}
</button>
))}
{error && <p role="alert">{error.message}</p>}
</>
);
};
export const App = () => (
<WalletManagerProvider config={config}>
<WalletPicker />
</WalletManagerProvider>
);WalletManagerProvider creates one manager per mount and starts it in an
effect, so nothing runs during a server render. To render persisted
connections from the first paint, pass initialState={readWalletSnapshot(cookies)}
from a Server Component, with a cookie-backed storage driver on the client.
Hooks
- Actions:
useWalletManager()returns the manager:connect,disconnect,disconnectAll,requestAccounts,setAccount,setActive,setSelectionandclearConnectionErrorare stable references, andgetState()reads without subscribing. - Connecting:
useConnect()returnsconnect(id), which never rejects (the outcome lands instatusanderror),connectAsync(id), which resolves the wallet or rejects with aConnectionError, plusreset,connectingId,statusanderror. - State:
useDiscoveredWallets,useConnectedWallets,useWallet(id?),useSelectedWallet(platform),useAccounts(id?),useConnectionStatus,useIsHydrated,useIsReconnecting(id?), anduseWalletState(selector)for anything else. Omitting the id reads the active wallet;nullreads none. - Grouped:
useDiscoveredWalletsByPlatformanduseConnectedWalletsByPlatform. - Async reads:
useSigner(wallet)anduseBalance(wallet, { account?, token? })take the entry fromuseWallet()oruseSelectedWallet(platform)and return{ data, error, status }, whereerroris always anError. They stay"idle"without a wallet and while it is reconnecting, anduseBalancestays idle for wallets withoutgetBalance.
Capabilities are checked by presence: render a "sign" button only when
wallet.connector.signMessage exists.
Documentation
License
MIT, copyright 2026 Pedro Filho.
