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 permissions
const appConfig = new AppConfig(['store_write', 'publish_data']);
const userSession = new UserSession({ appConfig });
// Connect wallet
function 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 name
icon: string; // URL to your app's icon
};
onFinish: () => void; // Called after successful connection
onCancel?: () => void; // Called if user cancels
userSession: UserSession;
network?: StacksNetwork;
}

Managing user sessions

After connecting, you can check authentication status and retrieve user data:

// Check if user is signed in
if (userSession.isUserSignedIn()) {
const userData = userSession.loadUserData();
console.log('User address:', userData.profile.stxAddress.testnet);
}
// Sign out
function disconnectWallet() {
userSession.signUserOut();
// Update your app's UI state
}

React implementation

Here's a complete React hook for wallet management:

components/use-wallet.ts
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 response
console.log('Auth response:', authResponse);
// The user's DID
console.log('User DID:', authResponse.userSession.loadUserData().decentralizedID);
// Profile data
const 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 storage
  • publish_data - Publish publicly readable data to Gaia
  • email - 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 blocked
useEffect(() => {
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.

Next steps