Token Input
An input field for entering token amounts with a token selector, balance display, and automatic number formatting
24.58
$3,996.73
import { TokenInput } from "@/components/sol/token-input";
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";
export function TokenInputDemo() {
return (
<TokenInput
tokens={[
{ icon: SOL_ICON, symbol: "SOL" },
{ icon: USDC_ICON, symbol: "USDC" },
]}
defaultToken="SOL"
balance="24.58"
usdValue="$3,996.73"
/>
);
}Installation
pnpm dlx shadcn@latest add @solanaui/token-input
npx shadcn@latest add @solanaui/token-input
yarn dlx shadcn@latest add @solanaui/token-input
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";
<TokenInput
tokens={[
{ icon: SOL_ICON, symbol: "SOL" },
{ icon: USDC_ICON, symbol: "USDC" },
]}
defaultToken="SOL"
balance="24.58"
/>Source Code
"use client";import { WalletIcon } from "lucide-react";import React from "react";import { NumericFormat } from "react-number-format";import { TokenCombobox } from "@/registry/sol/token-combobox";import { Button } from "@/components/ui/button";import { Input } from "@/components/ui/input";import { cn } from "@/lib/utils";interface TokenInputProps { tokens: { icon: string; symbol: string }[]; defaultToken?: string; balance?: string; value?: string; usdValue?: string; onValueChange?: (value: string) => void; onTokenSelect?: (token: { icon: string; symbol: string }) => void; className?: string;}const TokenInput = ({ tokens, defaultToken, balance, value, usdValue, onValueChange, onTokenSelect, className,}: TokenInputProps) => { const [internalValue, setInternalValue] = React.useState(value ?? ""); const currentValue = value ?? internalValue; const handleValueChange = (values: { value: string }) => { setInternalValue(values.value); onValueChange?.(values.value); }; const handleQuickAmount = (fraction: number) => { if (!balance) return; const numericBalance = Number.parseFloat(balance.replace(/,/g, "")); if (Number.isNaN(numericBalance)) return; const newValue = (numericBalance * fraction).toString(); setInternalValue(newValue); onValueChange?.(newValue); }; return ( <div className={cn( "flex flex-col gap-3 border p-4 rounded-lg w-full", className, )} > {balance && ( <div className="flex items-center justify-between"> <span className="inline-flex items-center gap-1.5 text-xs text-muted-foreground"> <WalletIcon className="size-3.5" /> {balance} </span> <div className="flex items-center gap-2"> <Button variant="outline" size="sm" className="text-xs h-6 px-2 rounded-sm" onClick={() => handleQuickAmount(0.5)} > Half </Button> <Button variant="outline" size="sm" className="text-xs h-6 px-2 rounded-sm" onClick={() => handleQuickAmount(1)} > Max </Button> </div> </div> )} <div className="flex items-center gap-2 w-full"> <TokenCombobox tokens={tokens} defaultValue={defaultToken} onSelect={onTokenSelect} /> <div className="flex flex-col flex-1 min-w-0 items-end"> <NumericFormat value={currentValue} onValueChange={handleValueChange} thousandSeparator="," decimalSeparator="." allowNegative={false} placeholder="0" inputMode="decimal" customInput={Input} className="text-right bg-transparent pr-1 dark:bg-transparent shadow-none border-none focus:ring-0 focus-visible:ring-0 w-full md:text-xl" /> {usdValue && ( <span className="text-xs text-muted-foreground pr-1"> {usdValue} </span> )} </div> </div> </div> );};export type { TokenInputProps };export { TokenInput };