Skip to Content

number-input

ui

Numeric input with increment and decrement buttons, min/max/step support.

Source Code

View the full component source code below.

"use client"

import * as React from "react"
import { MinusIcon, PlusIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"

interface NumberInputProps {
  value?: number
  onChange?: (value: number) => void
  min?: number
  max?: number
  step?: number
  disabled?: boolean
  className?: string
}

function NumberInput({
  value = 0,
  onChange,
  min = -Infinity,
  max = Infinity,
  step = 1,
  disabled = false,
  className,
}: NumberInputProps) {
  function clamp(n: number) {
    return Math.min(max, Math.max(min, n))
  }

  function decrement() {
    onChange?.(clamp(value - step))
  }

  function increment() {
    onChange?.(clamp(value + step))
  }

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    const parsed = parseFloat(e.target.value)
    if (!Number.isNaN(parsed)) {
      onChange?.(clamp(parsed))
    }
  }

  return (
    <div
      data-slot="number-input"
      className={cn(
        "inline-flex items-center rounded-4xl border border-input bg-input/30 transition-colors focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]",
        disabled && "pointer-events-none opacity-50",
        className
      )}
    >
      <Button
        type="button"
        variant="ghost"
        size="icon-sm"
        disabled={disabled || value <= min}
        onClick={decrement}
        aria-label="Decrease"
        className="rounded-l-4xl rounded-r-none"
      >
        <MinusIcon />
      </Button>
      <input
        type="text"
        inputMode="numeric"
        value={value}
        onChange={handleChange}
        disabled={disabled}
        aria-label="Number value"
        className="h-8 w-14 border-x border-border bg-transparent text-center text-sm outline-none"
      />
      <Button
        type="button"
        variant="ghost"
        size="icon-sm"
        disabled={disabled || value >= max}
        onClick={increment}
        aria-label="Increase"
        className="rounded-l-none rounded-r-4xl"
      >
        <PlusIcon />
      </Button>
    </div>
  )
}

export { NumberInput }
export type { NumberInputProps }

Installation

npx shadcn@latest add https://registry.mukoko.com/api/v1/ui/number-input

Dependencies

lucide-reactbutton

API

Fetch this component's metadata and source code from the registry API.

GET/api/v1/ui/number-input

Source

components/ui/number-input.tsx