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.
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.jsonRequirements
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-reactUsage
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
resolverOptionsgets 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.
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.
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.
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
| Prop | Type | Default | Description |
|---|---|---|---|
address | Address | - | EVM wallet address (required) |
name | string | null | - | Resolved name override |
avatarUrl | string | null | - | Avatar URL override |
resolveIdentity | boolean | true | Resolve ENS/Base name and avatar |
resolverOptions | OnchainResolverOptions | Base then ENS | Resolver clients and lookup order |
showAvatar | boolean | true | Show avatar or initials fallback |
truncate | boolean | true | Truncate the fallback address |
truncateChars | number | 4 | Characters shown at each end when fallback address is truncated |
showCopy | boolean | true | Show copy-to-clipboard behavior from AddressDisplay |
showExplorer | boolean | true | Show explorer link from AddressDisplay |
chainId | number | null | 1 | EVM chain id used to pick a known block explorer |
explorerUrl | string | Derived from chainId | Full explorer URL override for this address |
copyIcon | ReactNode | Lucide copy icon | Icon rendered before the label when copy is enabled |
explorerIcon | ReactNode | Lucide external link icon | Icon rendered for the explorer link |
className | string | - | Applied to the root wrapper |
avatarClassName | string | - | Applied to the avatar |
contentClassName | string | - | Applied to the inner AddressDisplay wrapper |
addressClassName | string | - | Applied to the address text or copy trigger |