Connect Button

Wallet connect button with modal and dropdown. Base variant includes address, copy, and disconnect.

Preview

"use client";import { formatAddress } from "@solana/connector";import { useConnector } from "@solana/connector/react";import { ChevronDown } from "lucide-react";import { useState } from "react";import { Button } from "@/components/ui/button";import {  DropdownMenu,  DropdownMenuContent,  DropdownMenuTrigger,} from "@/components/ui/dropdown-menu";import { Spinner } from "@/components/ui/spinner";import { cn } from "@/lib/utils";import {  WalletDropdownContent,  type WalletDropdownContentProps,} from "./wallet-dropdown-content";import { WalletIcon } from "./wallet-icon";import { WalletModal } from "./wallet-modal";interface ConnectButtonProps {  className?: string;  /**   * Custom dropdown content component.   * Defaults to base WalletDropdownContent (address + copy + disconnect).   * Pass a custom component to add balance, network switcher, tokens, etc.   *   * @example   * ```tsx   * <ConnectButton dropdownContent={WalletDropdownWithBalance} />   * ```   */  dropdownContent?: React.ComponentType<WalletDropdownContentProps>;}export function ConnectButton({  className,  dropdownContent: DropdownContent = WalletDropdownContent,}: ConnectButtonProps) {  const [isModalOpen, setIsModalOpen] = useState(false);  const [isDropdownOpen, setIsDropdownOpen] = useState(false);  const { isConnected, isConnecting, account, connector } = useConnector();  if (isConnected && account && connector) {    const shortAddress = formatAddress(account);    const walletIcon = connector.icon || undefined;    return (      <DropdownMenu open={isDropdownOpen} onOpenChange={setIsDropdownOpen}>        <DropdownMenuTrigger asChild>          <Button            variant="default"            size="lg"            className={cn("gap-2", className)}          >            <WalletIcon              src={walletIcon}              alt={connector.name}              className="h-5 w-5 shrink-0"            />            <span className="text-xs">{shortAddress}</span>            <ChevronDown              className={cn(                "h-4 w-4 opacity-50 transition-transform duration-200 ease-in-out",                isDropdownOpen && "-rotate-180",              )}            />          </Button>        </DropdownMenuTrigger>        <DropdownMenuContent          align="end"          side="bottom"          className="w-auto rounded-2xl p-0 shadow-md"        >          <DropdownContent            selectedAccount={account}            walletIcon={walletIcon}            walletName={connector.name}          />        </DropdownMenuContent>      </DropdownMenu>    );  }  return (    <>      <Button        size="lg"        variant="default"        onClick={() => setIsModalOpen(true)}        className={className}        disabled={isConnecting}      >        {isConnecting ? (          <>            <Spinner className="h-4 w-4" />            <span className="text-xs">Connecting...</span>          </>        ) : (          "Connect Wallet"        )}      </Button>      <WalletModal open={isModalOpen} onOpenChange={setIsModalOpen} />    </>  );}

Prerequisites

Complete the installation guide before using this component — it covers project setup, environment variables, and wiring up NitsoProvider.

Try the Demo

Install the demo to see it working immediately without any wiring:

npx shadcn@latest add https://nitso.fun/r/connect-button-demo.json
pnpm dlx shadcn@latest add https://nitso.fun/r/connect-button-demo.json
yarn dlx shadcn@latest add https://nitso.fun/r/connect-button-demo.json
bunx --bun shadcn@latest add https://nitso.fun/r/connect-button-demo.json

This installs connect-button-demo.tsx into your components/ folder. Drop it anywhere:

import { ConnectButtonDemo } from '@/components/connect-button-demo';

export default function Home() {
  return (
    <nav>
      <ConnectButtonDemo />
    </nav>
  );
}

Delete it when you're ready to wire things up yourself.

Installation

npx shadcn@latest add https://nitso.fun/r/connect-button.json
pnpm dlx shadcn@latest add https://nitso.fun/r/connect-button.json
yarn dlx shadcn@latest add https://nitso.fun/r/connect-button.json
bunx --bun shadcn@latest add https://nitso.fun/r/connect-button.json

Install dependencies

npm install @solana/connector @solana/web3.js
pnpm add @solana/connector @solana/web3.js
yarn add @solana/connector @solana/web3.js
bun add @solana/connector @solana/web3.js

Install shadcn UI components

npx shadcn@latest badge button dialog dropdown-menu accordion separator spinner
pnpm dlx shadcn@latest badge button dialog dropdown-menu accordion separator spinner
yarn dlx shadcn@latest badge button dialog dropdown-menu accordion separator spinner
bunx --bun shadcn@latest badge button dialog dropdown-menu accordion separator spinner

Copy the source code

"use client";import { formatAddress } from "@solana/connector";import { useConnector } from "@solana/connector/react";import { ChevronDown } from "lucide-react";import { useState } from "react";import { Button } from "@/components/ui/button";import {  DropdownMenu,  DropdownMenuContent,  DropdownMenuTrigger,} from "@/components/ui/dropdown-menu";import { Spinner } from "@/components/ui/spinner";import { cn } from "@/lib/utils";import {  WalletDropdownContent,  type WalletDropdownContentProps,} from "./wallet-dropdown-content";import { WalletIcon } from "./wallet-icon";import { WalletModal } from "./wallet-modal";interface ConnectButtonProps {  className?: string;  /**   * Custom dropdown content component.   * Defaults to base WalletDropdownContent (address + copy + disconnect).   * Pass a custom component to add balance, network switcher, tokens, etc.   *   * @example   * ```tsx   * <ConnectButton dropdownContent={WalletDropdownWithBalance} />   * ```   */  dropdownContent?: React.ComponentType<WalletDropdownContentProps>;}export function ConnectButton({  className,  dropdownContent: DropdownContent = WalletDropdownContent,}: ConnectButtonProps) {  const [isModalOpen, setIsModalOpen] = useState(false);  const [isDropdownOpen, setIsDropdownOpen] = useState(false);  const { isConnected, isConnecting, account, connector } = useConnector();  if (isConnected && account && connector) {    const shortAddress = formatAddress(account);    const walletIcon = connector.icon || undefined;    return (      <DropdownMenu open={isDropdownOpen} onOpenChange={setIsDropdownOpen}>        <DropdownMenuTrigger asChild>          <Button            variant="default"            size="lg"            className={cn("gap-2", className)}          >            <WalletIcon              src={walletIcon}              alt={connector.name}              className="h-5 w-5 shrink-0"            />            <span className="text-xs">{shortAddress}</span>            <ChevronDown              className={cn(                "h-4 w-4 opacity-50 transition-transform duration-200 ease-in-out",                isDropdownOpen && "-rotate-180",              )}            />          </Button>        </DropdownMenuTrigger>        <DropdownMenuContent          align="end"          side="bottom"          className="w-auto rounded-2xl p-0 shadow-md"        >          <DropdownContent            selectedAccount={account}            walletIcon={walletIcon}            walletName={connector.name}          />        </DropdownMenuContent>      </DropdownMenu>    );  }  return (    <>      <Button        size="lg"        variant="default"        onClick={() => setIsModalOpen(true)}        className={className}        disabled={isConnecting}      >        {isConnecting ? (          <>            <Spinner className="h-4 w-4" />            <span className="text-xs">Connecting...</span>          </>        ) : (          "Connect Wallet"        )}      </Button>      <WalletModal open={isModalOpen} onOpenChange={setIsModalOpen} />    </>  );}

Usage

import { ConnectButton } from "@/components/nitso/connect-button/";

export function Header() {
  return (
    <nav>
      <ConnectButton />
    </nav>
  );
}

Addons

The base dropdown shows address, copy, and disconnect. Install addons to extend it:

AddonDescription
wallet-balanceSOL balance display
network-switcherMainnet / Devnet / Testnet switcher
token-listSPL token holdings
transaction-listRecent transaction history
wallet-connect-modalWalletConnect QR code modal

See each addon's docs for wiring instructions.

Props

ConnectButton

PropTypeDefaultDescription
classNamestringAdditional CSS classes for the button
dropdownContentComponentType<WalletDropdownContentProps>WalletDropdownContentCustom dropdown component rendered when wallet is connected

WalletDropdownContent

PropTypeDefaultDescription
selectedAccountstringConnected wallet address (required)
walletIconstringWallet icon URL
walletNamestringWallet name e.g. Phantom (required)
actionsReactNodeExtra action buttons next to copy, e.g. NetworkSwitcherButton
childrenReactNodeContent between header and disconnect, e.g. WalletBalance

WalletModal

PropTypeDefaultDescription
openbooleanWhether the modal is visible (required)
onOpenChange(open: boolean) => voidCallback when open state changes (required)

On this page