shadimage
onchain-ui
uiamount-inputsource: ok · hit

Amount Input

Controlled decimal string input for token amounts. Preserves precision and incomplete edits, with an optional fractional digit limit.

npx shadcn add "https://onchain-ui.dev/r/amount-input.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light

Registry dependencies

input

Files1 · source cached 10/9/2026, 9:42:11 PM

registry/onchain-ui/amount-input.tsx
"use client";

import type { ComponentProps } from "react";
import { cn } from "@/lib/utils";
import { Input } from "@/components/ui/input";

export interface AmountInputProps
  extends Omit<
    ComponentProps<"input">,
    "type" | "inputMode" | "value" | "defaultValue" | "onChange" | "min" | "max" | "step"
  > {
  /** Unformatted decimal string. Empty and incomplete values are valid while editing. */
  value: string;
  /** Receives accepted edits without numeric conversion or rounding. */
  onValueChange?: (value: string) => void;
  /** Maximum fractional digits. Omit for unrestricted precision; 0 allows integers only. */
  decimals?: number;
}

/** Checks editing syntax, not whether an amount is ready for a transaction. */
export function isAmountInputValue(value: string, decimals?: number): boolean {
  if (decimals !== undefined && (!Number.isInteger(decimals) || decimals < 0)) {
    return false;
  }
  if (!/^[0-9]*(\.[0-9]*)?$/.test(value)) return false;
  const point = value.indexOf(".");
  return point === -1 || decimals === undefined ||
    (decimals > 0 && value.length - point - 1 <= decimals);
}

export function AmountInput({
  value,
  onValueChange,
  decimals,
  readOnly = !onValueChange,
  disabled,
  className,
  placeholder = "0",
  ...props
}: AmountInputProps) {
  return (
    <Input
      autoComplete="off"
      spellCheck={false}
      size={1}
      {...props}
      data-slot="amount-input"
      type="text"
      inputMode="decimal"
      value={value}
      placeholder={placeholder}
      readOnly={readOnly}
      disabled={disabled}
      onChange={(event) => {
        const next = event.target.value;
        if (!disabled && !readOnly && isAmountInputValue(next, decimals)) {
          onValueChange?.(next);
        }
      }}
      className={cn(
        "font-mono tabular-nums",
        className
      )}
    />
  );
}

More from onchain-ui

Browse all components in this library →