SolanaUISolanaUI

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