SolanaUISolanaUI

Token Icon Group

Stacked, overlapping token icons for displaying LP pairs, multi-token groups, or collateral baskets

SOL
USDC
SOL
USDC
BONK
SOL
USDC
BONK
JitoSOL
+2
import { TokenIconGroup } from "@/components/sol/token-icon-group";

const SOL_ICON =
  "https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/So11111111111111111111111111111111111111112/logo.png";
const USDC_ICON =
  "https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v/logo.png";

export function TokenIconGroupDemo() {
  return (
    <TokenIconGroup
      tokens={[
        { src: SOL_ICON, alt: "SOL" },
        { src: USDC_ICON, alt: "USDC" },
      ]}
      size={36}
    />
  );
}

Installation

pnpm dlx shadcn@latest add @solanaui/token-icon-group
npx shadcn@latest add @solanaui/token-icon-group
yarn dlx shadcn@latest add @solanaui/token-icon-group

Usage

LP Pair

<TokenIconGroup
  tokens={[
    { src: SOL_ICON, alt: "SOL" },
    { src: USDC_ICON, alt: "USDC" },
  ]}
/>

With Overflow Badge

When there are more tokens than max (defaults to 4), a +N badge is shown at the end.

<TokenIconGroup
  tokens={[
    { src: SOL_ICON, alt: "SOL" },
    { src: USDC_ICON, alt: "USDC" },
    { src: BONK_ICON, alt: "BONK" },
    { src: JITOSOL_ICON, alt: "JitoSOL" },
    { src: MSOL_ICON, alt: "mSOL" },
  ]}
  max={3}
/>

Source Code

import { TokenIcon } from "@/registry/sol/token-icon";import { cn } from "@/lib/utils";interface TokenIconGroupProps {  tokens: { src: string; alt?: string }[];  size?: number;  overlap?: number;  max?: number;  className?: string;}const TokenIconGroup = ({  tokens,  size = 24,  overlap = -10,  max = 4,  className,}: TokenIconGroupProps) => {  const visible = tokens.slice(0, max);  const remaining = tokens.length - max;  const hasOverlap = visible.length > 1 || remaining > 0;  return (    <div className={cn("flex items-center", className)}>      {visible.map((token, i) => (        <div          key={`${token.src}-${i}`}          className={cn(            "rounded-full relative leading-[0]",            hasOverlap && "ring-2 ring-background",          )}          style={{            marginLeft: i === 0 ? 0 : overlap,            zIndex: visible.length + 1 - i,          }}        >          <TokenIcon            src={token.src}            alt={token.alt ?? "Token"}            width={size}            height={size}          />        </div>      ))}      {remaining > 0 && (        <div          className="relative flex items-center justify-center rounded-full bg-muted text-muted-foreground ring-2 ring-background font-medium"          style={{            width: size,            height: size,            marginLeft: overlap,            fontSize: size * 0.35,            zIndex: visible.length + 2,          }}        >          +{remaining}        </div>      )}    </div>  );};export type { TokenIconGroupProps };export { TokenIconGroup };