Txn Table
A table for displaying blockchain transactions with signature links, action badges, and token info
| Signature | Time | Action | Token | Amount |
|---|---|---|---|---|
| 5UfD...3kPm | 2 minutes ago | Swap | 24.5 SOL$3,982.72 | |
| 8mRx...7jKn | 15 minutes ago | Transfer | 1,000 USDC$1,000.00 | |
| 3vPq...9hLr | about 1 hour ago | Stake | 100 SOL$16,256.00 |
import { TxnTable } from "@/components/sol/txn-table";
const SOL_ICON =
"https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/So11111111111111111111111111111111111111112/logo.png";
export function TxnTableDemo() {
return (
<TxnTable
transactions={[
{
signature: "5UfD...3kPm",
timestamp: new Date(Date.now() - 2 * 60 * 1000),
action: "Swap",
token: "SOL",
tokenIcon: SOL_ICON,
amount: "24.5 SOL",
value: "$3,982.72",
},
]}
/>
);
}Installation
pnpm dlx shadcn@latest add @solanaui/txn-table
npx shadcn@latest add @solanaui/txn-table
yarn dlx shadcn@latest add @solanaui/txn-table
Usage
<TxnTable
transactions={[
{
signature: "5UfD...3kPm",
timestamp: new Date(Date.now() - 2 * 60 * 1000),
action: "Swap",
token: "SOL",
tokenIcon: SOL_ICON,
amount: "24.5 SOL",
value: "$3,982.72",
},
]}
/>Source Code
import { formatDistanceToNow } from "date-fns";import { ExternalLinkIcon } from "lucide-react";import { Badge } from "@/components/ui/badge";import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow,} from "@/components/ui/table";import { cn } from "@/lib/utils";import { TokenIcon } from "@/registry/sol/token-icon";interface TxnTableProps { transactions: { signature: string; timestamp: Date; action: string; token: string; tokenIcon?: string; amount: string; value?: string; explorerUrl?: string; }[]; className?: string;}const truncateSignature = (sig: string) => { if (sig.length <= 12) return sig; return `${sig.slice(0, 4)}...${sig.slice(-4)}`;};const TxnTable = ({ transactions, className }: TxnTableProps) => { return ( <Table className={className}> <TableHeader> <TableRow> <TableHead>Signature</TableHead> <TableHead>Time</TableHead> <TableHead>Action</TableHead> <TableHead>Token</TableHead> <TableHead className="text-right">Amount</TableHead> </TableRow> </TableHeader> <TableBody> {transactions.map((txn, i) => { const explorerUrl = txn.explorerUrl ?? `https://solscan.io/tx/${txn.signature}`; return ( <TableRow key={`${txn.signature}-${i}`}> <TableCell> <a href={explorerUrl} target="_blank" rel="noopener noreferrer" className={cn( "inline-flex items-center gap-1 font-mono text-xs", "text-muted-foreground hover:text-foreground transition-colors", )} > {truncateSignature(txn.signature)} <ExternalLinkIcon className="size-3" /> </a> </TableCell> <TableCell className="text-muted-foreground text-sm"> {formatDistanceToNow(txn.timestamp, { addSuffix: true })} </TableCell> <TableCell> <Badge variant="secondary" className="capitalize text-xs"> {txn.action} </Badge> </TableCell> <TableCell> <div className="flex items-center gap-1.5"> {txn.tokenIcon && ( <TokenIcon src={txn.tokenIcon} alt={txn.token} width={18} height={18} /> )} <span className="font-medium">{txn.token}</span> </div> </TableCell> <TableCell className="text-right"> <div className="flex flex-col items-end"> <span className="font-medium">{txn.amount}</span> {txn.value && ( <span className="text-xs text-muted-foreground"> {txn.value} </span> )} </div> </TableCell> </TableRow> ); })} </TableBody> </Table> );};export type { TxnTableProps };export { TxnTable };