Connect Wallet
Connecting a wallet is the first step in any KEA Wallet integration. When your app calls connect(), the SDK opens a modal overlay where the user authenticates with their passkey and selects which account(s) to share with your dApp. No browser extension is required — everything runs inside a secure iframe.
This guide covers three integration approaches: a drop-in React component, React hooks for custom UI, and the vanilla JavaScript SDK.
- You have installed the SDK packages — see Quickstart
- You know which package to use — see Integration Options
How Connection Works
- Your app calls
connect()(or the user clicks<KeaConnectButton />) - The SDK creates a hidden iframe pointing to the wallet's embedded page
- A full-screen modal overlay appears over your app
- The user authenticates via passkey inside the modal
- The user selects which account(s) to authorize for your dApp
- The modal closes and your app receives a
ConnectResultwith the authorized accounts - The SDK emits a
connectevent and transitions to the connected state
connect() auto-initializes the SDK. You can call initialize() beforehand to pre-load the iframe for a faster first connection.
React Integration
For React apps, KEA Wallet provides a context provider, hooks, and pre-built UI components. Choose the approach that matches your UI requirements.
Set Up the Provider
All React approaches require wrapping your app with KeaProvider:
import { KeaProvider } from '@kea-wallet/react-sdk';
const config = {
rpcUrl: 'https://grpc-web.alphanet.thruput.org',
autoConnect: true,
};
function App() {
return (
<KeaProvider config={config}>
<YourApp />
</KeaProvider>
);
}
See BrowserSDKConfig for all available config options.
Option A: Pre-built Connect Button
The fastest path — a single component that handles all visual states automatically:
import { KeaConnectButton } from '@kea-wallet/react-ui';
function Toolbar() {
return <KeaConnectButton />;
}
The button handles four states:
| State | Button text | Behavior |
|---|---|---|
| Disconnected | "Connect Wallet" | Click opens the wallet modal |
| Connecting | "Connecting..." | Disabled while modal is open |
| Reconnecting | "Reconnecting..." | Disabled during auto-reconnect |
| Connected | Truncated address (e.g. taJO...yrKk) | Click to disconnect |
<KeaConnectButton /> uses dark-themed inline styles that are not customizable via CSS. For branded or custom-styled buttons, use Option B below.
See KeaConnectButton API reference.
Option B: Custom UI with Hooks
For full control over styling and behavior, use the useWallet() hook:
import { useWallet } from '@kea-wallet/react-sdk';
function ConnectButton() {
const { connect, disconnect, isConnected, isConnecting, selectedAccount } = useWallet();
const handleConnect = async () => {
try {
await connect({
metadata: {
appName: 'My App',
appUrl: 'https://myapp.com',
imageUrl: 'https://myapp.com/logo.png',
},
});
} catch (err) {
console.error('Connection failed:', err);
}
};
if (isConnected) {
return (
<div>
<p>Connected: {selectedAccount?.address}</p>
<button onClick={() => disconnect()}>Disconnect</button>
</div>
);
}
return (
<button onClick={handleConnect} disabled={isConnecting}>
{isConnecting ? 'Connecting...' : 'Connect Wallet'}
</button>
);
}
Key points:
metadatais optional. When omitted,appNameandappUrlare auto-derived fromwindow.location. ProvidingimageUrlis recommended for a polished consent screen.isConnectingistruewhile the modal is open — always use it to disable the button and show loading feedback.connect()returns aPromise<ConnectResult>— if the user rejects or closes the modal, the promise throws. Use try/catch as shown above, or handle errors via theerrorevent.
See UseWalletReturn and ConnectOptions for all available properties.
Vanilla JavaScript
For non-React apps (or when you need full lifecycle control), use BrowserSDK directly:
import { BrowserSDK } from '@kea-wallet/browser-sdk';
// 1. Create the SDK instance
const sdk = new BrowserSDK({
rpcUrl: 'https://grpc-web.alphanet.thruput.org',
autoConnect: true,
});
// 2. Initialize (pre-loads the iframe for faster connect)
await sdk.initialize();
// 3. Connect on user click
connectButton.addEventListener('click', async () => {
try {
const result = await sdk.connect({
metadata: { appName: 'My App' },
});
const accounts = sdk.getAccounts();
const selected = sdk.getSelectedAccount();
console.log('Connected:', selected.address);
} catch (err) {
console.error('Connection failed:', err);
}
});
initialize()is optional —connect()calls it automatically if needed. Calling it early pre-loads the iframe for faster UX.- If the user rejects the connection or closes the modal,
connect()throws an error. - After connecting, use
getAccounts()andgetSelectedAccount()to access account data.
When your app unmounts or the page unloads, call sdk.destroy() to tear down the iframe and remove event listeners. Failing to do so may cause memory leaks.
See BrowserSDKConfig for all configuration options.
Session Persistence
When autoConnect is enabled, the SDK automatically restores the wallet session on page reload — no modal is shown:
- On connect, the SDK stores a hint in
localStorage(kea_wallet_state) - On page reload, it detects the hint and silently queries the wallet iframe to check if the session is still valid
- If valid, the SDK transitions to connected state without showing the modal
- If the session has expired or storage is blocked, the SDK stays disconnected — no error is thrown
- React
- Vanilla JS
function ConnectButton() {
const { connect, isConnected, isConnecting, isReconnecting } = useWallet();
if (isReconnecting) {
return <span>Restoring session...</span>;
}
if (isConnected) {
return <span>Connected</span>;
}
return (
<button onClick={() => connect()} disabled={isConnecting}>
{isConnecting ? 'Connecting...' : 'Connect Wallet'}
</button>
);
}
const sdk = new BrowserSDK({
rpcUrl: 'https://grpc-web.alphanet.thruput.org',
autoConnect: true,
});
await sdk.initialize(); // triggers auto-reconnect if hint exists
sdk.on('reconnecting', ({ isReconnecting }) => {
console.log('Reconnecting:', isReconnecting);
});
sdk.on('connect', (result) => {
console.log('Session restored:', sdk.getSelectedAccount()?.address);
});
Use isReconnecting to show a skeleton or "Restoring session..." placeholder instead of briefly flashing the "Connect Wallet" button on every page load.
Disconnecting
Calling disconnect() ends the wallet session, clears internal state, emits a disconnect event, and updates the localStorage hint so auto-connect will not fire on next reload.
- React
- Vanilla JS
const { disconnect } = useWallet();
<button onClick={() => disconnect()}>Disconnect</button>
await sdk.disconnect();
// Session is cleared, 'disconnect' event fired
// When completely done with the SDK:
sdk.destroy();
disconnect() ends the session but keeps the SDK instance alive. Call destroy() only when you are completely done with the SDK (e.g., page unload in a vanilla JS app). In React, KeaProvider handles cleanup automatically on unmount.
UX Best Practices
- User-initiated click required —
connect()opens a modal overlay. Browsers may block it if not triggered by a direct user interaction (click/tap). Never callconnect()on page load. - Always show loading state — Use
isConnecting(React) or track your own boolean (vanilla JS) to disable the connect button and show "Connecting..." while the modal is open. - Handle reconnection gracefully — When
autoConnectis enabled, show a skeleton or neutral state duringisReconnectinginstead of flashing the connect button. - Provide app metadata — While
metadatais optional, providing an explicitappNameandimageUrlimproves the consent screen experience. - Handle rejection — The user can close the modal or reject the connection. Wrap
connect()in a try/catch or handle the error state. See the Error Handling guide.
What's Next
- Handle Accounts — Work with multiple accounts and switch the active account
- Sign Transactions — Build and sign transactions using the wallet
- SDK Events — Subscribe to connection, disconnection, and account change events
- Error Handling — Handle user rejection, timeouts, and connection failures