Action Box
A generic single-input action form for staking, lending, depositing, bridging, and more
Stake
24.58
APY7.2%
Annual Rewards~1.77 SOL
ValidatorHelius
import { ActionBox } from "@/components/sol/action-box";
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 ActionBoxDemo() {
return (
<ActionBox
tokens={[
{ icon: SOL_ICON, symbol: "SOL" },
{ icon: USDC_ICON, symbol: "USDC" },
]}
defaultToken="SOL"
balance="24.58"
label="Stake"
details={[
{ label: "APY", value: "7.2%", className: "text-emerald-500" },
{ label: "Annual Rewards", value: "~1.77 SOL" },
{ label: "Validator", value: "Helius" },
]}
submitLabel="Stake SOL"
/>
);
}Installation
pnpm dlx shadcn@latest add @solanaui/action-box
npx shadcn@latest add @solanaui/action-box
yarn dlx shadcn@latest add @solanaui/action-box
Usage
Staking
<ActionBox
tokens={[{ icon: SOL_ICON, symbol: "SOL" }]}
defaultToken="SOL"
balance="24.58"
label="Stake"
details={[
{ label: "APY", value: "7.2%", className: "text-emerald-500" },
{ label: "Annual Rewards", value: "~1.77 SOL" },
]}
submitLabel="Stake SOL"
/>Lending Supply
<ActionBox
tokens={[{ icon: USDC_ICON, symbol: "USDC" }]}
defaultToken="USDC"
balance="1,250.00"
label="Supply"
details={[
{ label: "Supply APY", value: "8.45%" },
{ label: "Utilization", value: "78.4%" },
]}
submitLabel="Supply USDC"
/>Bridge
<ActionBox
tokens={[{ icon: SOL_ICON, symbol: "SOL" }]}
defaultToken="SOL"
balance="24.58"
label="Bridge"
details={[
{ label: "Destination", value: "Ethereum" },
{ label: "Fee", value: "0.001 SOL" },
{ label: "Est. Time", value: "~15 min" },
]}
submitLabel="Bridge SOL"
/>Source Code
import { TokenInput } from "@/registry/sol/token-input";import { Button } from "@/components/ui/button";import { Separator } from "@/components/ui/separator";import type { DetailRow } from "@/registry/lib/types";import { cn } from "@/lib/utils";type ActionBoxDetail = DetailRow;interface ActionBoxProps { tokens: { icon: string; symbol: string }[]; defaultToken?: string; balance?: string; label?: string; details?: ActionBoxDetail[]; submitLabel?: string; onSubmit?: () => void; className?: string;}const ActionBox = ({ tokens, defaultToken, balance, label, details, submitLabel = "Submit", onSubmit, className,}: ActionBoxProps) => { return ( <div className={cn("flex flex-col gap-4 border rounded-lg p-4", className)}> {label && ( <span className="text-sm font-medium text-muted-foreground"> {label} </span> )} <TokenInput tokens={tokens} defaultToken={defaultToken} balance={balance} /> {details && details.length > 0 && ( <> <Separator /> <div className="flex flex-col gap-1.5 text-sm"> {details.map((detail) => ( <div key={detail.label} className="flex justify-between"> <span className="text-muted-foreground">{detail.label}</span> <span className={detail.className}>{detail.value}</span> </div> ))} </div> </> )} <Button className="w-full" size="lg" onClick={onSubmit}> {submitLabel} </Button> </div> );};export type { ActionBoxProps, ActionBoxDetail };export { ActionBox };