Connect wallet
Integrate Stacks wallets into your web application
Overview
Connecting to a Stacks wallet allows your app to interact with the blockchain through the user's wallet. This includes authenticating users, requesting transaction signatures, and accessing account information.
Stacks.js provides a unified interface that works with all major Stacks wallets including Hiro Wallet, Xverse, and others that implement the wallet interface standard.
Basic connection
Here's the simplest way to connect a wallet:
import { showConnect } from '@stacks/connect';import { UserSession, AppConfig } from '@stacks/connect';// Configure app permissionsconst appConfig = new AppConfig(['store_write', 'publish_data']);const userSession = new UserSession({ appConfig });// Connect walletfunction connectWallet() {showConnect({appDetails: {name: 'My Stacks App',icon: '/logo.png',},onFinish: () => {console.log('Wallet connected successfully!');},userSession,});}
Connection options
The showConnect
function accepts various options to customize the connection flow:
interface ConnectOptions {appDetails: {name: string; // Your app's display nameicon: string; // URL to your app's icon};onFinish: () => void; // Called after successful connectiononCancel?: () => void; // Called if user cancelsuserSession: UserSession;network?: StacksNetwork;}
Managing user sessions
After connecting, you can check authentication status and retrieve user data:
// Check if user is signed inif (userSession.isUserSignedIn()) {const userData = userSession.loadUserData();console.log('User address:', userData.profile.stxAddress.testnet);}// Sign outfunction disconnectWallet() {userSession.signUserOut();// Update your app's UI state}
React implementation
Here's a complete React hook for wallet management:
import { useState, useEffect } from 'react';import { showConnect, UserSession, AppConfig } from '@stacks/connect';const appConfig = new AppConfig(['store_write', 'publish_data']);const userSession = new UserSession({ appConfig });export function useWallet() {const [isConnected, setIsConnected] = useState(false);const [address, setAddress] = useState<string | null>(null);useEffect(() => {if (userSession.isUserSignedIn()) {const userData = userSession.loadUserData();setIsConnected(true);setAddress(userData.profile.stxAddress.testnet);}}, []);const connect = () => {showConnect({appDetails: {name: 'My App',icon: '/logo.png',},onFinish: () => {const userData = userSession.loadUserData();setIsConnected(true);setAddress(userData.profile.stxAddress.testnet);},userSession,});};const disconnect = () => {userSession.signUserOut();setIsConnected(false);setAddress(null);};return { isConnected, address, connect, disconnect };}
Use the hook in your components:
function WalletButton() {const { isConnected, address, connect, disconnect } = useWallet();if (isConnected) {return (<div><p>Connected: {address}</p><button onClick={disconnect}>Disconnect</button></div>);}return <button onClick={connect}>Connect Wallet</button>;}
Advanced connection flow
For more control over the connection process, you can handle specific wallet events:
showConnect({appDetails: {name: 'My App',icon: '/logo.png',},onFinish: (authResponse) => {// Access the full authentication responseconsole.log('Auth response:', authResponse);// The user's DIDconsole.log('User DID:', authResponse.userSession.loadUserData().decentralizedID);// Profile dataconst profile = authResponse.userSession.loadUserData().profile;console.log('Username:', profile.name);},onCancel: () => {console.log('User cancelled connection');// Handle cancellation in your UI},userSession,});
Permission scopes
When creating your AppConfig
, you can request different permission scopes:
const appConfig = new AppConfig(['store_write', // Store encrypted data'publish_data', // Publish data to Gaia'email', // Request user's email (if available)]);
Available scopes:
store_write
- Read and write encrypted data to Gaia storagepublish_data
- Publish publicly readable data to Gaiaemail
- Request access to user's email address
Handling connection errors
Implement proper error handling for connection failures:
async function safeConnect() {try {await showConnect({appDetails: {name: 'My App',icon: '/logo.png',},onFinish: () => {console.log('Connected successfully');},userSession,});} catch (error) {console.error('Connection failed:', error);// Show error message to user}}
Network-specific connections
You can specify which network to use during connection:
import { StacksMainnet, StacksTestnet } from '@stacks/network';showConnect({appDetails: {name: 'My App',icon: '/logo.png',},network: new StacksTestnet(), // or new StacksMainnet()onFinish: () => {console.log('Connected to testnet');},userSession,});
Best practices
- Persist sessions: User sessions persist across page reloads automatically
- Handle loading states: Show appropriate UI while checking authentication status
- Network awareness: Always indicate which network (mainnet/testnet) is active
- Error boundaries: Implement error boundaries to catch wallet interaction failures
- Mobile support: Test wallet connections on mobile devices where behavior may differ
Troubleshooting
Wallet popup blocked
Some browsers block popups. Ensure showConnect
is called in response to user action:
// ✓ Good - direct user action<button onClick={() => showConnect({...})}>Connect</button>// ✗ Bad - may be blockeduseEffect(() => {showConnect({...}); // Popup blockers will prevent this}, []);
Multiple wallet detection
When multiple wallets are installed, the user will see a selection screen. This is handled automatically by the wallet interface.