Wallet & Web3.js
How to connect Phantom/Solflare and read on-chain data in a Next.js app.
Install
npm install @solana/wallet-adapter-react @solana/wallet-adapter-react-ui \
@solana/wallet-adapter-wallets @solana/web3.js
Provider Setup
Wrap your app in the wallet providers. In Next.js App Router, this goes in a client component.
// app/providers.tsx
"use client";
import {
ConnectionProvider,
WalletProvider,
} from "@solana/wallet-adapter-react";
import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
import { PhantomWalletAdapter, SolflareWalletAdapter } from "@solana/wallet-adapter-wallets";
import { clusterApiUrl } from "@solana/web3.js";
import "@solana/wallet-adapter-react-ui/styles.css";
const endpoint = clusterApiUrl("mainnet-beta");
// or: "https://mainnet.helius-rpc.com/?api-key=YOUR_KEY"
const wallets = [
new PhantomWalletAdapter(),
new SolflareWalletAdapter(),
];
export function Providers({ children }: { children: React.ReactNode }) {
return (
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={wallets} autoConnect>
<WalletModalProvider>
{children}
</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
);
}
// app/layout.tsx
import { Providers } from "./providers";
export default function RootLayout({ children }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
Connect Button
import { WalletMultiButton } from "@solana/wallet-adapter-react-ui";
export function ConnectWallet() {
return <WalletMultiButton />;
}
Or build a custom button:
"use client";
import { useWallet } from "@solana/wallet-adapter-react";
import { useWalletModal } from "@solana/wallet-adapter-react-ui";
export function CustomConnectButton() {
const { publicKey, disconnect, connected } = useWallet();
const { setVisible } = useWalletModal();
if (connected && publicKey) {
const short = `${publicKey.toBase58().slice(0, 4)}...${publicKey.toBase58().slice(-4)}`;
return (
<button onClick={disconnect}>
{short} (disconnect)
</button>
);
}
return <button onClick={() => setVisible(true)}>Connect Wallet</button>;
}
Read SOL Balance
"use client";
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
import { useEffect, useState } from "react";
export function SolBalance() {
const { connection } = useConnection();
const { publicKey } = useWallet();
const [balance, setBalance] = useState<number | null>(null);
useEffect(() => {
if (!publicKey) return;
connection.getBalance(publicKey).then((lamports) => {
setBalance(lamports / LAMPORTS_PER_SOL);
});
}, [publicKey, connection]);
if (!publicKey) return <p>Not connected</p>;
return <p>{balance?.toFixed(4)} SOL</p>;
}
Read SPL Token Balance
import { getAssociatedTokenAddress, getAccount } from "@solana/spl-token";
import { Connection, PublicKey } from "@solana/web3.js";
async function getSPLBalance(
connection: Connection,
mintAddress: string,
ownerAddress: string
): Promise<number> {
const mint = new PublicKey(mintAddress);
const owner = new PublicKey(ownerAddress);
const ata = await getAssociatedTokenAddress(mint, owner);
const account = await getAccount(connection, ata);
// amount is BigInt, divide by decimals (USDC = 6, most SPL = 9)
return Number(account.amount) / 10 ** 6;
}
Transaction: Send SOL
import {
Connection, PublicKey, Transaction,
SystemProgram, sendAndConfirmTransaction
} from "@solana/web3.js";
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
async function sendSOL(toAddress: string, amountSOL: number) {
const { connection } = useConnection();
const { publicKey, sendTransaction } = useWallet();
if (!publicKey) throw new Error("Wallet not connected");
const to = new PublicKey(toAddress);
const lamports = amountSOL * LAMPORTS_PER_SOL;
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: publicKey,
toPubkey: to,
lamports,
})
);
const signature = await sendTransaction(transaction, connection);
await connection.confirmTransaction(signature, "confirmed");
return signature;
}
Last updated: September 2026.