What is Bitcoin Connect?
Bitcoin Connect is an open-source library from Alby that adds Lightning wallet connectivity to any web app. Users click a button, connect their wallet (Alby Hub, NWC, or any WebLN-compatible wallet), and your app can send and receive Lightning payments.
No wallet-specific code. No custody. The user brings their own wallet, and Bitcoin Connect handles the protocol layer.
What You'll Build
A React app that:
- Shows a "Connect Wallet" button
- Creates Lightning invoices and displays a payment modal
- Sends outgoing payments via the connected wallet
- Persists the wallet connection across page reloads
Prerequisites
- Node.js 18+
- A React or Next.js project (we'll use Vite + React for simplicity)
- A Lightning wallet that supports NWC or WebLN (Alby Hub, Alby Extension, or any NWC wallet)
Step 1: Install Bitcoin Connect
npm install @getalby/bitcoin-connect-react
For a plain HTML/JS project, use the web components package instead:
npm install @getalby/bitcoin-connect
Step 2: Initialize Bitcoin Connect
Call init() once, early in your app's lifecycle. This sets up the global configuration that all Bitcoin Connect components use.
// src/main.tsx (or App.tsx)
import { init } from '@getalby/bitcoin-connect-react';
init({
appName: 'My Lightning App', // shown to the user during connection
showBalance: true, // display wallet balance after connecting
persistConnection: true, // remember the wallet across sessions
});
Configuration options:
appName— displayed in wallet connection promptsfilters— restrict to specific connector types, e.g.["nwc"]showBalance— show the connected wallet's balancepersistConnection— save the connection in localStorage
Step 3: Add the Connect Button
The <Button /> component renders a connect/disconnect button. When clicked, it opens a modal where the user picks their wallet.
import { Button } from '@getalby/bitcoin-connect-react';
function App() {
return (
<div>
<h1>My Lightning App</h1>
<Button
onConnect={(provider) => {
console.log('Wallet connected!', provider);
}}
onDisconnect={() => {
console.log('Wallet disconnected');
}}
/>
</div>
);
}
The provider object implements the WebLN interface. You can use it to send payments, create invoices, and check balances.
Step 4: Accept Payments with PayButton
The <PayButton /> shows a payment modal for a specific invoice. Use this when you want users to pay for something.
import { PayButton } from '@getalby/bitcoin-connect-react';
function PaymentPage({ invoice }) {
return (
<PayButton
invoice={invoice}
onPaid={(response) => {
console.log('Payment confirmed! Preimage:', response.preimage);
// Grant access, ship the product, etc.
}}
/>
);
}
The payment modal handles everything: QR code, copy button, wallet payment prompt, and confirmation. Your code only needs to react to the onPaid callback.
Step 5: Create Invoices Programmatically
For dynamic pricing (tips, donations, pay-per-use), create invoices on the fly using the launchPaymentModal function:
import { launchPaymentModal } from '@getalby/bitcoin-connect-react';
async function handleTip() {
// Create an invoice server-side (your API)
const res = await fetch('/api/create-invoice', {
method: 'POST',
body: JSON.stringify({ amount: 1000, memo: 'Tip for great content' }),
});
const { invoice } = await res.json();
// Launch the payment modal
const { setPaid } = launchPaymentModal({
invoice,
onPaid: ({ preimage }) => {
console.log('Tip received!', preimage);
},
onCancelled: () => {
console.log('User cancelled');
},
});
}
Step 6: Send Payments from the Connected Wallet
Once a user connects their wallet, you can send outgoing payments using the provider:
import { requestProvider } from '@getalby/bitcoin-connect-react';
async function payInvoice(bolt11Invoice) {
try {
const provider = await requestProvider();
const result = await provider.sendPayment(bolt11Invoice);
console.log('Paid! Preimage:', result.preimage);
return result;
} catch (err) {
console.error('Payment failed:', err);
throw err;
}
}
The requestProvider() call will prompt the user to connect if they haven't already. Once connected, sendPayment() sends the payment through their wallet.
Step 7: Listen for Connection State
React to connection state changes anywhere in your app:
import { onConnected, onDisconnected } from '@getalby/bitcoin-connect-react';
// Fires whenever a wallet connects
onConnected((provider) => {
console.log('Connected with provider:', provider);
});
// Fires when the wallet disconnects
onDisconnected(() => {
console.log('Disconnected');
});
Next.js: Dynamic Import
Bitcoin Connect uses browser APIs that don't work during server-side rendering. In Next.js, use dynamic imports:
import dynamic from 'next/dynamic';
const BitcoinConnectButton = dynamic(
() => import('@getalby/bitcoin-connect-react').then((mod) => mod.Button),
{ ssr: false }
);
const BitcoinConnectPayButton = dynamic(
() => import('@getalby/bitcoin-connect-react').then((mod) => mod.PayButton),
{ ssr: false }
);
// Use in your component:
// <BitcoinConnectButton onConnect={...} />
// <BitcoinConnectPayButton invoice={invoice} onPaid={...} />
Call init() inside a useEffect to ensure it runs client-side only:
import { useEffect } from 'react';
export default function Layout({ children }) {
useEffect(() => {
import('@getalby/bitcoin-connect-react').then(({ init }) => {
init({ appName: 'My Next.js App', persistConnection: true });
});
}, []);
return <div>{children}</div>;
}
Complete Example: Tip Jar
Here's a minimal tip jar component that creates invoices and handles payments:
import { useState } from 'react';
import { Button, launchPaymentModal } from '@getalby/bitcoin-connect-react';
function TipJar() {
const [amount, setAmount] = useState(1000);
async function handleTip() {
// Replace with your actual invoice creation endpoint
const res = await fetch('/api/invoice', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount, memo: 'Lightning tip' }),
});
const { payment_request } = await res.json();
launchPaymentModal({
invoice: payment_request,
onPaid: () => alert('Thanks for the tip!'),
});
}
return (
<div>
<Button />
<div style={{ marginTop: '1rem' }}>
{[100, 1000, 5000].map((sats) => (
<button
key={sats}
onClick={() => { setAmount(sats); handleTip(); }}
style={{ margin: '0 0.5rem', padding: '0.5rem 1rem' }}
>
{sats} sats
</button>
))}
</div>
</div>
);
}
Available Components
| Component | Purpose |
|---|---|
<Button /> | Connect/disconnect wallet (opens modal) |
<PayButton /> | Pay a specific invoice (opens payment modal) |
<Connect /> | Inline wallet connection UI (no modal) |
<Payment /> | Inline payment UI (no modal) |
What's Next
- Bitcoin Connect GitHub — source code, issues, and examples
- Building a Lightning Service with NWC — server-side payment handling
- Add Lightning Tipping in 60 Seconds — even simpler tipping with no code
- Alby Developer Guide — full NWC and Alby SDK documentation
Bitcoin Connect gives your users wallet choice without vendor lock-in. One library, every Lightning wallet, zero custody.