onchain-ui
Components

Chain select

Network selector for wallet switching or filtering, with optional all-networks and unsupported-chain states.

ChainSelect lists the chains you pass in and calls onSelect with the selected chain ID. Your app handles wallet connections and network switching.

Loading...
chain-select.tsxShow code
import { ChainSelect } from "@/components/ui/chain-select"const chains = [  { id: 1, name: "Ethereum" },  { id: 8453, name: "Base" },  { id: 42161, name: "Arbitrum One" },  { id: 10, name: "Optimism" },  { id: 137, name: "Polygon" },]export function ChainSelectDemo() {  const [chainId, setChainId] = useState<number | null>(8453)  return (    <ChainSelect chains={chains} value={chainId} onSelect={setChainId} />  )}

Installation

npx shadcn add @onchain-ui/chain-select
Open in

Requirements

ChainSelect uses the shadcn dropdown-menu. The install adds components/ui/dropdown-menu.tsx if it is missing, or uses your existing file. It also installs NetworkLogo for the chain logos.

Usage

import { ChainSelect } from "@/components/ui/chain-select"

<ChainSelect chains={chains} value={chainId} onSelect={setChainId} />

Control the selection through value and onSelect. ChainSelect does not store connection state or make network calls.

To use it as a filter, set allLabel and onClear to add an all-networks item. Use null for the unfiltered state:

const [chainId, setChainId] = useState<number | null>(null)

<ChainSelect
  chains={chains}
  value={chainId}
  allLabel="All networks"
  menuLabel="Filter by network"
  onClear={() => setChainId(null)}
  onSelect={setChainId}
/>

Wiring with wagmi

Read the available chains with useChains and call useSwitchChain when the user selects one:

"use client"

import { useChains, useConnection, useSwitchChain } from "wagmi"
import { ChainSelect } from "@/components/ui/chain-select"

export function ChainSwitcher() {
  const { chainId } = useConnection()
  const chains = useChains()
  const switchChain = useSwitchChain()

  return (
    <ChainSelect
      chains={chains.map((chain) => ({
        id: chain.id,
        name: chain.name,
        badge: chain.testnet ? "Testnet" : undefined,
      }))}
      value={chainId ?? null}
      pendingChainId={switchChain.isPending ? switchChain.variables?.chainId : null}
      onSelect={(id) => {
        const next = chains.find((chain) => chain.id === id)
        if (next) switchChain.mutate({ chainId: next.id })
      }}
    />
  )
}

onSelect returns a number. If you augment wagmi's Register type, switchChain expects one of your configured chain IDs. The example looks up the selected chain to narrow the type before calling mutate.

The snippet targets wagmi 3. On wagmi 2, read chainId from useAccount() and the chain list from useSwitchChain().chains. Call switchChain({ chainId }) instead of switchChain.mutate(...). ChainSelect itself does not import wagmi.

Unsupported networks

A wallet can connect to a network your app does not support. When value is set to an ID absent from chains, the trigger shows "Unsupported network" with warning styles. The menu still lists your supported chains so the user can switch.

Loading...
chain-select-unsupported.tsxShow code
import { ChainSelect } from "@/components/ui/chain-select"export function ChainSelectUnsupported() {  // The wallet is on a chain this app does not support.  const [chainId, setChainId] = useState<number | null>(5315)  return (    <ChainSelect chains={chains} value={chainId} onSelect={setChainId} />  )}

Pass unsupportedLabel to change the warning text.

Examples

Disconnected

value={null} shows the placeholder and reserves space for the network logo.

Loading...
chain-select-disconnected.tsxShow code
import { ChainSelect } from "@/components/ui/chain-select"export function ChainSelectDisconnected() {  const [chainId, setChainId] = useState<number | null>(null)  return (    <ChainSelect chains={chains} value={chainId} onSelect={setChainId} />  )}

Switching

pendingChainId marks the chain being switched to and locks the control while the wallet prompt is open.

Loading...
chain-select-pending.tsxShow code
import { ChainSelect } from "@/components/ui/chain-select"export function ChainSelectPending() {  return (    <ChainSelect      chains={chains}      value={8453}      pendingChainId={42161}      onSelect={switchChain}    />  )}

Sizes

Use showLabel={false} for a logo-only trigger in tight headers. The accessible name falls back to the network name.

Loading...
chain-select-sizes.tsxShow code
import { ChainSelect } from "@/components/ui/chain-select"export function ChainSelectSizes() {  return (    <div className="flex items-center gap-3">      <ChainSelect chains={chains} value={chainId} onSelect={setChainId} size="sm" />      <ChainSelect chains={chains} value={chainId} onSelect={setChainId} />      <ChainSelect chains={chains} value={chainId} onSelect={setChainId} showLabel={false} />    </div>  )}

Props

PropTypeDefaultDescription
chainsChainSelectOption[]-Selectable chains, usually the ones your app is configured for
valuenumber | null-Currently connected chain id
onSelect(chainId: number) => void-Called with the chain id the user picked
onClear() => void-Called when the optional all-networks item is picked
allLabelstring-Adds an item that clears the selected chain
pendingChainIdnumber | null-Chain being switched to. Blocks further selection
disabledbooleanfalseDisable the whole control
placeholderstring"Select network"Trigger label when value is null
unsupportedLabelstring"Unsupported network"Trigger label when value is missing from chains
menuLabelReactNode"Switch network"Heading above the menu items
showLabelbooleantrueShow the network name in the trigger
size"sm" | "default""default"Visual size
align"start" | "center" | "end""start"Menu alignment against the trigger
classNamestring-Applied to the trigger
contentClassNamestring-Applied to the menu

ChainSelectOption

PropTypeDefaultDescription
idnumber-EVM chain id
namestring | nullBuilt-in network nameDisplay name
logoSrcstring | nullBuilt-in chain logoNetwork image URL override
symbolstring | nullBuilt-in network symbolShort symbol for the logo fallback
disabledbooleanfalseRender but do not allow selection
badgeReactNode-Rendered after the name, e.g. a "Testnet" badge

On this page