Token Combobox
A combobox component for selecting tokens with search functionality
import { TokenCombobox } from "@/components/sol/token-combobox";
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 tokens = [
{ icon: SOL_ICON, symbol: "SOL" },
{ icon: USDC_ICON, symbol: "USDC" },
];
export function TokenComboboxDemo() {
return <TokenCombobox tokens={tokens} />;
}Installation
pnpm dlx shadcn@latest add @solanaui/token-combobox
npx shadcn@latest add @solanaui/token-combobox
yarn dlx shadcn@latest add @solanaui/token-combobox
Usage
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";
<TokenCombobox
tokens={[
{ icon: SOL_ICON, symbol: "SOL" },
{ icon: USDC_ICON, symbol: "USDC" },
]}
/>Source Code
"use client";import { Check, ChevronsUpDown } from "lucide-react";import React from "react";import { TokenIcon } from "@/registry/sol/token-icon";import { Button } from "@/components/ui/button";import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList,} from "@/components/ui/command";import { Popover, PopoverContent, PopoverTrigger,} from "@/components/ui/popover";import { cn } from "@/lib/utils";interface TokenComboboxProps { tokens: { icon: string; symbol: string; }[]; defaultValue?: string; onSelect?: (token: { icon: string; symbol: string }) => void; className?: string;}const TokenCombobox = ({ tokens, defaultValue, onSelect, className,}: TokenComboboxProps) => { const [open, setOpen] = React.useState(false); const [value, setValue] = React.useState(defaultValue ?? ""); const activeToken = tokens.find( (token) => token.symbol.toLowerCase() === value.toLowerCase(), ); return ( <Popover open={open} onOpenChange={setOpen}> <PopoverTrigger asChild> <Button variant="outline" role="combobox" aria-expanded={open} className={cn("shrink-0 justify-between", className)} > {activeToken ? ( <div className="flex items-center gap-2.5"> <TokenIcon src={activeToken.icon} alt={activeToken.symbol} width={20} height={20} /> {activeToken.symbol} </div> ) : ( "Select token..." )} <ChevronsUpDown className="opacity-50" /> </Button> </PopoverTrigger> <PopoverContent className="w-[200px] p-0"> <Command> <CommandInput placeholder="Search token..." className="h-9" /> <CommandList> <CommandEmpty>No tokens found.</CommandEmpty> <CommandGroup> {tokens.map((token) => ( <CommandItem key={token.symbol} value={token.symbol} onSelect={(currentValue) => { const newValue = currentValue === value ? "" : currentValue; setValue(newValue); setOpen(false); if (newValue) { const selected = tokens.find( (t) => t.symbol.toLowerCase() === newValue.toLowerCase(), ); if (selected) onSelect?.(selected); } }} > <TokenIcon src={token.icon} alt={token.symbol} width={20} height={20} /> {token.symbol} <Check className={cn( "ml-auto", value.toLowerCase() === token.symbol.toLowerCase() ? "opacity-100" : "opacity-0", )} /> </CommandItem> ))} </CommandGroup> </CommandList> </Command> </PopoverContent> </Popover> );};export type { TokenComboboxProps };export { TokenCombobox };