web3icons

Chain logos in network switchers

Use chain logos in network selectors and badges, pair them with names, tell testnets apart from mainnets that share a logo, and handle unknown chains.

5 min read

The network switcher is one of the few controls in a dApp where a mistake costs money. Bridging to the wrong chain, signing on a testnet you thought was mainnet, or sending tokens on a network the recipient doesn't use all start with someone misreading which network is active. Chain logos help people read it faster, but only if they're used carefully.

This guide covers where chain logos appear, why they always need a name next to them, how to handle testnets that share their mainnet's logo, and what to show for a chain you don't recognize.

Where chain logos show up

A typical multichain dApp uses chain logos in four places, each with its own size:

  • The switcher button in the header: a 20 to 24 px logo with the network name, showing which network is active.
  • The network list that opens from it: 24 to 32 px logos with names, one row per chain.
  • Badges on token icons: a 12 to 16 px chain logo overlapping the corner of a 32 to 40 px token logo, saying which chain that balance is on. The network badges guide covers their geometry.
  • Transaction and activity rows, where the chain is context rather than the main information.

The logos themselves are easy to source. Web3 Icons has a network section with logos for more than 250 networks, such as Ethereum, Base and Arbitrum One. The harder part is using them so nobody misreads them.

Always pair the logo with the name

Chain logos are less familiar than token logos. Many are abstract circles or geometric marks, several networks in the same ecosystem use similar shapes, and new chains launch constantly. Even experienced users may know a dozen chain logos by sight, not fifty.

So the name belongs next to the logo wherever space allows, in the button and in every row of the list. On narrow screens, a switcher button can shrink to the logo alone, but it still needs an accessible name, and the network list it opens should show names in full:

<button type="button" className="network-button">
  <img src={chain.icon} width={24} height={24} alt="" />
  <span className="network-name">{chain.name}</span>
  <span className="visually-hidden">, change network</span>
</button>

The image has empty alt because the text already names the network. On small screens, hide .network-name with a visually-hidden class rather than display: none, so screen readers still get it. The button's accessible name, "Ethereum, change network", starts with the visible text, which keeps it usable for people who operate the page by voice (WCAG 2.5.3 Label in Name).

Testnets usually don't have their own logos. Sepolia, the public Ethereum testnet most apps use, shares Ethereum's logo, and an L2's testnet, such as Base Sepolia, uses the L2's logo. Web3 Icons follows the same convention: testnets share their mainnet's logo, and searching for a testnet name like "Sepolia" leads you to the mainnet logo.

That makes the logo useless for the one distinction that matters most: is this real money? Distinguish testnets with other signals, and use more than one:

  • The name. Always show the full testnet name, "Base Sepolia", never just "Base."
  • A text tag. A small "Testnet" label next to the name in the list and in the switcher button.
  • A visual treatment of the logo. Desaturating it, lowering its opacity, or adding a dashed ring sets testnets apart at a glance. It supports the text; it doesn't replace it, since desaturation is easy to miss, especially for people with color vision deficiencies and for logos that are already mostly gray. The color alone guide explains why.
  • Separate grouping. List testnets in their own section below mainnets, or hide them unless the user turns on a testnet mode.
  • A persistent banner while the app is connected to a testnet, so every screen says so.

In code, the distinction belongs in your chain configuration, not in scattered conditionals:

type Chain = { id: number; name: string; icon: string; testnet?: boolean }

export const CHAINS: Chain[] = [
  { id: 1, name: 'Ethereum', icon: '/chains/ethereum.svg' },
  { id: 8453, name: 'Base', icon: '/chains/base.svg' },
  { id: 42161, name: 'Arbitrum One', icon: '/chains/arbitrum-one.svg' },
  { id: 11155111, name: 'Sepolia', icon: '/chains/ethereum.svg', testnet: true },
  { id: 84532, name: 'Base Sepolia', icon: '/chains/base.svg', testnet: true },
]

export function ChainOption({ chain }: { chain: Chain }) {
  return (
    <span className="chain-option">
      <img src={chain.icon} width={24} height={24} alt="" className={chain.testnet ? 'is-testnet' : undefined} />
      <span>{chain.name}</span>
      {chain.testnet && <span className="tag">Testnet</span>}
    </span>
  )
}
.is-testnet {
  filter: grayscale(1);
  opacity: 0.7;
}

Key everything by chain ID, never by name or logo. Two entries share an icon file here, and that's correct.

Unknown and unsupported networks

Wallets let people add any EVM chain, and dApps can suggest one with wallet_addEthereumChain from EIP-3085. That request includes an optional iconUrls field, but wallets decide whether to show those icons, and you shouldn't display an icon URL supplied by an arbitrary site in your own interface.

When a user's wallet is on a chain your app doesn't support, show:

  • A neutral placeholder, such as a gray circle, not a guess at a logo.
  • The text "Unsupported network" and, if you have it, the chain's name and ID, for example "Chain 12345".
  • A clear action: a button to switch to a supported network, which calls wallet_switchEthereumChain (EIP-3326).

Don't fall back to the Ethereum logo for every unknown EVM chain. It looks like a reassuring answer to a question you couldn't answer. The network switching UX guide covers the full flow, including what happens while the wallet asks for confirmation.

States in the network list

The list needs the same care as any menu:

  • Selected. Mark the active network with a checkmark and a text cue such as "Connected", not only a background highlight.
  • Switching. After a click, the wallet asks the user to confirm, and that can take a while. Show "Switching to Base, confirm in your wallet" on the button, and keep the old network shown as active until the wallet reports the change.
  • Unavailable. If a network is listed but not usable for this action, for example a swap pair that doesn't exist there, disable it and say why.

Logos make a network list faster to scan. Names, tags and states make it safe to use, and in a control this close to people's funds, that matters more. For apps that span many chains, the multichain design guide covers the patterns beyond the switcher.

More in Using crypto logos

All guides