SolanaUISolanaUI

Pool Card

A versatile card for displaying a single token or multi-token pool with metrics, sparkline, and action slot

SOL
Solana
$162.56
6.82% APY
+9.69%
+9.69%
SOL
USDC
SOL/USDC
TVL$245.8M
Volume (24h)$18.2M
APY12.4%
Fee0.25%
import { PoolCard } from "@/components/sol/pool-card";
import { TrendBadge } from "@/components/sol/trend-badge";
import { Button } from "@/components/ui/button";

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";

const SOL_SPARKLINE = [
  { time: "2026-01-01", value: 148.2 },
  { time: "2026-01-02", value: 151.4 },
  { time: "2026-01-03", value: 149.8 },
  { time: "2026-01-04", value: 153.6 },
  { time: "2026-01-05", value: 156.1 },
  { time: "2026-01-06", value: 154.3 },
  { time: "2026-01-07", value: 158.9 },
  { time: "2026-01-08", value: 157.2 },
  { time: "2026-01-09", value: 160.4 },
  { time: "2026-01-10", value: 162.56 },
];

export function PoolCardDemo() {
  return (
  <div className="grid grid-cols-1 sm:grid-cols-2 gap-4 w-full max-w-2xl mx-auto">
      {/* Single token */}
      <PoolCard
        tokens={[{ icon: SOL_ICON, symbol: "SOL" }]}
        name="Solana"
        price="$162.56"
        description="6.82% APY"
        series={SOL_SPARKLINE}
      >
        <TrendBadge>+9.69%</TrendBadge>
      </PoolCard>

      {/* Multi-token pool */}
      <PoolCard
        tokens={[
          { icon: SOL_ICON, symbol: "SOL" },
          { icon: USDC_ICON, symbol: "USDC" },
        ]}
        metrics={[
          { label: "TVL", value: "$245.8M" },
          { label: "Volume (24h)", value: "$18.2M" },
          { label: "APY", value: "12.4%", className: "text-emerald-500" },
          { label: "Fee", value: "0.25%" },
        ]}
      >
        <Button variant="outline" size="sm" className="w-full">
          Add Liquidity
        </Button>
      </PoolCard>
    </div>
  );
}

Installation

pnpm dlx shadcn@latest add @solanaui/pool-card
npx shadcn@latest add @solanaui/pool-card
yarn dlx shadcn@latest add @solanaui/pool-card

Usage

Single Token

Display a single asset with price, description, and sparkline chart.

const SOL_SPARKLINE = [
  { time: "2026-01-01", value: 148.2 },
  { time: "2026-01-03", value: 149.8 },
  { time: "2026-01-05", value: 156.1 },
  { time: "2026-01-07", value: 158.9 },
  { time: "2026-01-10", value: 162.56 },
];

<PoolCard
  tokens={[{ icon: SOL_ICON, symbol: "SOL" }]}
  name="Solana"
  price="$162.56"
  description="6.82% APY"
  series={SOL_SPARKLINE}
>
  <TrendBadge>+9.69%</TrendBadge>
</PoolCard>

Multi-Token Pool with Metrics

Display a liquidity pool with overlapping token icons and a metrics grid.

<PoolCard
  tokens={[
    { icon: SOL_ICON, symbol: "SOL" },
    { icon: USDC_ICON, symbol: "USDC" },
  ]}
  metrics={[
    { label: "TVL", value: "$245.8M" },
    { label: "Volume (24h)", value: "$18.2M" },
    { label: "APY", value: "12.4%", className: "text-emerald-500" },
    { label: "Fee", value: "0.25%" },
  ]}
>
  <Button variant="outline" size="sm" className="w-full">
    Add Liquidity
  </Button>
</PoolCard>

Highlight Metric

Make a key metric visually prominent with highlight: true. Useful for lending/yield cards where APY is the primary value.

<PoolCard
  tokens={[{ icon: SOL_ICON, symbol: "SOL" }]}
  name="Solana"
  price="$162.56"
  metrics={[
    { label: "Supply APY", value: "6.82%", highlight: true, className: "text-emerald-500" },
    { label: "TVL", value: "$420.8M" },
    { label: "Utilization", value: "78.4%" },
  ]}
/>

Custom Name

Override the auto-generated name (defaults to joining token symbols with "/").

<PoolCard
  tokens={[
    { icon: SOL_ICON, symbol: "SOL" },
    { icon: USDC_ICON, symbol: "USDC" },
  ]}
  name="SOL-USDC Concentrated"
  metrics={[
    { label: "TVL", value: "$245.8M" },
    { label: "APY", value: "12.4%", className: "text-emerald-500" },
  ]}
/>

Source Code

import type React from "react";import { SparklineChart } from "@/registry/sol/sparkline-chart";import { TokenIconGroup } from "@/registry/sol/token-icon-group";import {  Card,  CardContent,  CardDescription,  CardFooter,  CardHeader,  CardTitle,} from "@/components/ui/card";import { cn } from "@/lib/utils";interface PoolCardMetric {  label: string;  value: string;  highlight?: boolean;  className?: string;}interface PoolCardProps extends React.ComponentProps<typeof Card> {  tokens: { icon: string; symbol: string }[];  name?: string;  price?: string;  description?: string;  metrics?: PoolCardMetric[];  series?: { time: string; value: number }[];  children?: React.ReactNode;}const PoolCard = ({  tokens,  name,  price,  description,  metrics,  series,  children,  className,  ...props}: PoolCardProps) => {  const displayName = name ?? tokens.map((t) => t.symbol).join("/");  const iconTokens = tokens.map((t) => ({ src: t.icon, alt: t.symbol }));  const iconSize = tokens.length > 1 ? 28 : 36;  const iconOverlap = tokens.length > 1 ? -10 : 0;  const highlightMetric = metrics?.find((m) => m.highlight);  const regularMetrics = metrics?.filter((m) => !m.highlight);  return (    <Card className={cn("p-4 w-full", className)} {...props}>      <CardHeader className="p-0">        <CardTitle className="flex items-center justify-between gap-2">          <div className="flex items-center gap-2 text-xl">            <TokenIconGroup              tokens={iconTokens}              size={iconSize}              overlap={iconOverlap}            />            {displayName}          </div>          {price && <span>{price}</span>}        </CardTitle>        {description && (          <CardDescription className="text-left">{description}</CardDescription>        )}      </CardHeader>      {highlightMetric && (        <CardContent className="p-0">          <div className="flex flex-col">            <span className="text-muted-foreground text-xs">              {highlightMetric.label}            </span>            <span              className={cn(                "text-2xl font-bold tracking-tight",                highlightMetric.className,              )}            >              {highlightMetric.value}            </span>          </div>        </CardContent>      )}      {regularMetrics && regularMetrics.length > 0 && (        <CardContent className="p-0">          <div className="grid grid-cols-2 gap-x-6 gap-y-2 text-sm">            {regularMetrics.map((metric) => (              <div key={metric.label} className="flex flex-col">                <span className="text-muted-foreground text-xs">                  {metric.label}                </span>                <span className={cn("font-medium", metric.className)}>                  {metric.value}                </span>              </div>            ))}          </div>        </CardContent>      )}      {series && series.length > 0 && (        <CardContent className="p-0">          <SparklineChart series={series} />        </CardContent>      )}      {children && <CardFooter className="p-0">{children}</CardFooter>}    </Card>  );};export type { PoolCardProps, PoolCardMetric };export { PoolCard };