You Might Also Like
Web3 Wallet Connect Button — Free HTML CSS JS Snippet
Web3 Wallet Connect Button · Buttons · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Web3 Wallet Connect Button — Provider Picker, Simulated Connection State & Account Dropdown

Every decentralized application (dApp) needs some version of the "Connect Wallet" button — the entry point through which a user links their browser-based crypto wallet to a website so it can read their address, network, and balances, and request transaction signatures. This snippet builds the complete front-end interaction for that flow: a provider picker, a simulated connecting state, and a connected account menu with copy-to-clipboard and disconnect actions — entirely in vanilla HTML, CSS, and JS, with no real blockchain calls, since this is a UI pattern demo rather than a working Web3 integration.
The four-state button
The button cycles through four states tracked by a single state variable: disconnected (shows "Connect Wallet"), picking (the provider dropdown is open), connecting (a spinner replaces the label for roughly one second to simulate the real latency of a wallet extension responding to a connection request), and connected (the button itself becomes the truncated address with a green status dot). Each state transition calls renderButton(), which swaps the button's inner HTML and CSS classes to reflect the current state — this centralizes all visual logic in one function rather than scattering conditional class toggles across multiple event handlers.
Provider selection without real trademarked assets
Clicking the button while disconnected opens a dropdown listing three common wallet providers by name — MetaMask, Coinbase Wallet, and WalletConnect — each represented with a simple inline SVG glyph and a distinct accent color rather than the providers' actual trademarked logos, since reproducing real brand marks isn't appropriate for a generic demo component. Selecting any row calls selectProvider(), which immediately transitions to the connecting state and schedules a setTimeout that resolves to connected after 1000ms — standing in for the real async round-trip where a browser extension like MetaMask would pop up its own permission prompt and the page would await a promise from window.ethereum.request({ method: 'eth_requestAccounts' }).
Truncated address and network badge
Once connected, the button displays truncate(fullAddress), which slices the first 6 and last 4 characters of the mock address (0x71C7656EC7ab88b098defB751B7401B5f6d8976 becomes 0x71C7...8976) — the universal convention for displaying Ethereum-style addresses in constrained UI space, since full 42-character addresses are unreadable inline. The account dropdown additionally shows the network the wallet is connected to via a small badge with a colored status dot, which matters because the same address can hold entirely different assets on different chains (Ethereum mainnet vs. an L2 like Arbitrum), and users need to see at a glance which network a dApp believes they're on before approving any transaction.
Real clipboard copy, no fake behavior
The "Copy Address" action in the connected account menu uses the genuine browser Clipboard API — navigator.clipboard.writeText(fullAddress) — rather than faking the copy interaction, so this piece of the demo is fully functional exactly as it would be in production. The button label briefly changes to "Copied!" for 1.5 seconds via a setTimeout-driven revert, giving the user immediate, unambiguous confirmation that the action succeeded.
Why this matters for 2026 trust-driven UX
Wallet-connection flows sit at the highest-stakes trust boundary in any product: a user is about to grant a website visibility into their on-chain identity and, eventually, transaction-signing permission. Interfaces here benefit enormously from trust-driven UX principles — visible, unambiguous state (never leave the user wondering "am I connected or not?"), clearly labeled network context, and a disconnect action that's always one click away. This snippet's explicit state machine, visible spinner during the async gap, and persistent status dot are all in service of that same principle: never leave connection state ambiguous.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Paste this snippet into an AI coding assistant like Claude and ask it to walk through the four-state model (disconnected/picking/connecting/connected) and exactly how renderButton() maps each state to markup, plus why the outside-click and Escape-key handlers are attached at the document level rather than on the dropdowns themselves. It's a great starting point to extend with real Web3 integration — ask the assistant to wire selectProvider() up to an actual window.ethereum.request call for MetaMask, add proper error handling for the case where a user rejects the connection request in their wallet extension, or add a "wrong network" warning state that appears when the connected wallet's chain ID doesn't match the dApp's expected network.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a "Connect Wallet" button UI in plain HTML, CSS, and JavaScript that simulates a typical Web3 dApp wallet-connection flow, without making any real blockchain or extension calls.
Requirements:
- A single button that cycles through four distinct visual states driven by one explicit state variable: disconnected ("Connect Wallet"), a provider-picking state where a dropdown is open, a connecting state with a visible loading spinner replacing the button label, and a connected state showing a truncated mock address plus a small colored status indicator.
- Clicking the button while disconnected opens a dropdown listing at least three mock wallet provider options, each with a distinct simple icon (do not use real trademarked logo images) and provider name.
- Selecting a provider closes the dropdown, enters the connecting state for roughly one second (simulated delay), then transitions to the connected state showing a truncated version of a hardcoded mock address (first 6 and last 4 characters, e.g. 0x71C7...8976) plus a network badge (e.g. "Ethereum") with a colored dot.
- Clicking the button again while connected opens a second dropdown (not the provider picker) showing the full mock address, the network badge, a "Copy Address" action, and a "Disconnect" action.
- Copy Address must use the real browser Clipboard API to copy the full address, and must show a brief "Copied!" confirmation state on the button/label before reverting.
- Disconnect must reset all state back to the initial disconnected button and close any open dropdown.
- Clicking anywhere outside the widget, or pressing Escape, must close any open dropdown without changing the connection state.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Click Connect Wallet to open the provider pickertoggleProviderPicker() opens #provider-dropdown and transitions state to 'picking'. The dropdown lists MetaMask, Coinbase Wallet, and WalletConnect as mock options, each a .provider-row button with a data-provider attribute.
- 2Select a provider to simulate connectingClicking any .provider-row calls selectProvider(name), which closes the dropdown, sets state to 'connecting' (showing a spinning .spinner element in the button), and after a 1000ms setTimeout transitions to 'connected', rendering the truncated address.
- 3Open the account menu once connectedClicking the connected button toggles #account-dropdown instead of the provider picker, showing the full address, a conic-gradient avatar, and an "Ethereum" network badge with a green status dot.
- 4Copy the address with the real Clipboard APIThe "Copy Address" row calls copyAddress(), which awaits navigator.clipboard.writeText(fullAddress) and briefly changes the label to "Copied!" for 1.5 seconds as confirmation, falling back to a "Copy failed" message if the clipboard write throws.
- 5Disconnect to reset stateClicking "Disconnect" calls disconnect(), which resets state to 'disconnected', closes all dropdowns, and re-renders the button back to its original "Connect Wallet" label.
- 6Wire in a real Web3 providerReplace the setTimeout in selectProvider() with an actual await window.ethereum.request({ method: "eth_requestAccounts" }) call for MetaMask-compatible wallets, or integrate the official WalletConnect or Coinbase Wallet SDKs for their respective flows, then set fullAddress from the real returned account.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
No — this is a UI pattern demo with a hardcoded mock address and a setTimeout standing in for the real connection round-trip. To connect a real wallet, replace the setTimeout inside selectProvider() with an actual call such as await window.ethereum.request({ method: 'eth_requestAccounts' }) for MetaMask-compatible browser wallets, or integrate the WalletConnect or Coinbase Wallet SDK for their respective connection flows, then read the real returned account address instead of the hardcoded fullAddress constant.
Ethereum-style addresses are 42 characters long (0x plus 40 hex characters), far too long to display inline in a button or compact menu without wrapping or truncating other UI. The convention adopted across virtually every wallet and dApp is to show the first 6 and last 4 characters — enough for a user to visually recognize their own address at a glance while keeping the UI compact. The full, untruncated address is always available in the account dropdown for copying.
MetaMask, Coinbase Wallet, and WalletConnect logos are trademarked brand assets with usage guidelines controlling exactly how and where they may be reproduced. A generic, reusable UI snippet shouldn't bundle or imply endorsement from real brands, so this component uses simple inline SVG glyphs with distinct accent colors as visual stand-ins. In a real product integrating an official wallet SDK, you would typically use that SDK's provided icon assets under its actual usage terms.
It calls the standard browser Clipboard API: navigator.clipboard.writeText(fullAddress). This API requires a secure context (HTTPS or localhost) and, in most browsers, a direct user gesture like a click — which this implementation satisfies since it's called from a click event handler. No special permission prompt is needed for writing to the clipboard, unlike reading from it, which some browsers do gate behind a permission request.
After a real wallet connects, use a library like ethers.js or viem to call provider.getBalance(address) for the native token balance, and provider.lookupAddress(address) (or an ENS-specific library call) to resolve a human-readable .eth name if one exists. Render the ENS name in place of the truncated address when available, since users generally find it far more memorable and trustworthy than a raw hex string.