Pool Card
A versatile card for displaying a single token or multi-token pool with metrics, sparkline, and action slot
6.82% APY
+9.69%
+9.69%
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 };