Alphaonchain-ui is experimental. Components, docs, and registry URLs may change before a stable release.
onchain-ui
Components

Address Identity

Resolves an EVM address to an ENS or Base name with optional avatar, copy-to-clipboard, and explorer link.

Use AddressIdentity when an address should feel like a person or account, not only a hex string. It composes AddressDisplay, so the displayed label can resolve while copy and explorer behavior still use the underlying address.

Loading...
address-identity.tsxShow code
import { AddressIdentity } from "@/components/ui/address-identity"export function AddressIdentityDemo() {  return (    <AddressIdentity address="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" />  )}

Installation

npx shadcn add https://onchain-ui.dev/r/address-identity.json
Open in

Requirements

Add <Toaster /> from sonner to your root layout so copy feedback can show a toast.

import { Toaster } from "@/components/ui/sonner"

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Toaster />
      </body>
    </html>
  )
}

Dependencies

The registry item uses viem, sonner, and lucide-react.

npm install viem sonner lucide-react

Usage

import { AddressIdentity } from "@/components/ui/address-identity"

<AddressIdentity address="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" />

By default, AddressIdentity tries Base reverse resolution first, then ENS reverse resolution. If a name resolves, it also attempts to resolve the avatar record. If nothing resolves, it falls back to the truncated address.

Caching

Lookups are cached in memory for the session, so many rows showing the same address resolve once — concurrent lookups for the same address share a single in-flight request. Two details worth knowing:

  • Failures are not cached. A lookup that fails (rate-limited or unreachable RPC) is retried on the next mount instead of pinning "no name" for the rest of the session. Only genuine results — including "this address has no name" — stay cached.
  • Caches are scoped per resolver config. Each distinct set of clients in resolverOptions gets its own cache, so results from different RPC endpoints never mix, and custom clients keep full request deduplication.

Because the cache is scoped to the client instances, create clients once at module scope and reuse them. Building clients inline on every render still works, but each new instance starts with an empty cache.

Custom RPC endpoints

The default resolvers use each chain's public RPC endpoint, which is rate limited. Fine for demos and prototypes; in production, pass your own viem clients through resolverOptions.

import { createPublicClient, http } from "viem"
import { base, mainnet } from "viem/chains"

// Module scope: every component instance shares these clients and their cache.
const resolverOptions = {
  mainnetClient: createPublicClient({
    chain: mainnet,
    transport: http("https://your-rpc-provider.example/mainnet"),
  }),
  baseClient: createPublicClient({
    chain: base,
    transport: http("https://your-rpc-provider.example/base"),
  }),
}

<AddressIdentity address="0x..." resolverOptions={resolverOptions} />

Both clients are optional — pass only mainnetClient to keep the default Base endpoint, or only baseClient to keep the default mainnet endpoint.

Examples

Resolved Name

Pass name and avatarUrl when you already have profile data from your own API or indexer.

Loading...
address-identity-resolved.tsxShow code
import { AddressIdentity } from "@/components/ui/address-identity"export function AddressIdentityResolved() {  return (    <AddressIdentity      address="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"      name="vitalik.eth"      avatarUrl="https://metadata.ens.domains/mainnet/avatar/vitalik.eth"    />  )}

Fallback Only

Disable resolution when you want deterministic rendering or already know there is no profile.

Loading...
address-identity-fallback.tsxShow code
import { AddressIdentity } from "@/components/ui/address-identity"export function AddressIdentityFallback() {  return (    <AddressIdentity      address="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"      resolveIdentity={false}    />  )}

No Avatar

Hide the avatar when the identity sits inside dense tables or compact nav.

Loading...
address-identity-no-avatar.tsxShow code
import { AddressIdentity } from "@/components/ui/address-identity"export function AddressIdentityNoAvatar() {  return (    <AddressIdentity      address="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"      name="vitalik.eth"      showAvatar={false}    />  )}

Resolver Order

Use resolverOptions when your app wants ENS before Base Names, or when you want to pass custom viem clients.

<AddressIdentity
  address="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
  resolverOptions={{
    reverseLookupOrder: ["ens", "basename"],
  }}
/>

Props

PropTypeDefaultDescription
addressAddress-EVM wallet address (required)
namestring | null-Resolved name override
avatarUrlstring | null-Avatar URL override
resolveIdentitybooleantrueResolve ENS/Base name and avatar
resolverOptionsOnchainResolverOptionsBase then ENSResolver clients and lookup order
showAvatarbooleantrueShow avatar or initials fallback
truncatebooleantrueTruncate the fallback address
truncateCharsnumber4Characters shown at each end when fallback address is truncated
showCopybooleantrueShow copy-to-clipboard behavior from AddressDisplay
showExplorerbooleantrueShow explorer link from AddressDisplay
chainIdnumber | null1EVM chain id used to pick a known block explorer
explorerUrlstringDerived from chainIdFull explorer URL override for this address
copyIconReactNodeLucide copy iconIcon rendered before the label when copy is enabled
explorerIconReactNodeLucide external link iconIcon rendered for the explorer link
classNamestring-Applied to the root wrapper
avatarClassNamestring-Applied to the avatar
contentClassNamestring-Applied to the inner AddressDisplay wrapper
addressClassNamestring-Applied to the address text or copy trigger

On this page