Number Input

Adjusts numerical values via buttons, text entry, or dragging.

import { Label } from "@/components/ui/label";
import {
  NumberInput,
  NumberInputDecrement,
  NumberInputField,
  NumberInputGroup,
  NumberInputIncrement,
  NumberInputScrubArea,
} from "@/components/ui/number-input";
 
export function NumberInputDemo() {
  return (
    <NumberInput defaultValue={0} className="max-w-3xs">
      <NumberInputScrubArea>
        <Label className="cursor-ew-resize">Age</Label>
      </NumberInputScrubArea>
      <NumberInputGroup>
        <NumberInputDecrement />
        <NumberInputField />
        <NumberInputIncrement />
      </NumberInputGroup>
    </NumberInput>
  );
}

Installation

npx shadcn@latest add "https://eo-n.vercel.app/r/number-input"

Usage

Import all parts and piece them together.

import {
  NumberInput,
  NumberInputDecrement,
  NumberInputField,
  NumberInputGroup,
  NumberInputIncrement,
  NumberInputScrubArea,
  NumberInputScrubAreaCursor,
} from "@/components/ui/number-input";
<NumberInput>
  <NumberInputScrubArea>
    <Label>Age</Label>
    <NumberInputScrubAreaCursor />
  </NumberInputScrubArea>
  <NumberInputGroup>
    <NumberInputDecrement />
    <NumberInputField />
    <NumberInputIncrement />
  </NumberInputGroup>
</NumberInput>

Examples

Bounded Number Input

import { Minus, Plus } from "lucide-react";
 
import { Label } from "@/components/ui/label";
import {
  NumberInput,
  NumberInputDecrement,
  NumberInputField,
  NumberInputGroup,
  NumberInputIncrement,
  NumberInputScrubArea,
} from "@/components/ui/number-input";
 
export function NumberInputMinMax() {
  return (
    <NumberInput min={0} max={100} defaultValue={0} className="max-w-3xs">
      <NumberInputScrubArea>
        <Label className="cursor-ew-resize">Rating</Label>
      </NumberInputScrubArea>
      <NumberInputGroup>
        <NumberInputDecrement>
          <Minus />
        </NumberInputDecrement>
        <NumberInputField />
        <NumberInputIncrement>
          <Plus />
        </NumberInputIncrement>
      </NumberInputGroup>
    </NumberInput>
  );
}

Disabled

import { Minus, Plus } from "lucide-react";
 
import { Label } from "@/components/ui/label";
import {
  NumberInput,
  NumberInputDecrement,
  NumberInputField,
  NumberInputGroup,
  NumberInputIncrement,
  NumberInputScrubArea,
} from "@/components/ui/number-input";
 
export function NumberInputDisabled() {
  return (
    <NumberInput defaultValue={23} disabled className="max-w-3xs">
      <NumberInputScrubArea>
        <Label className="cursor-ew-resize">Age</Label>
      </NumberInputScrubArea>
      <NumberInputGroup>
        <NumberInputDecrement>
          <Minus />
        </NumberInputDecrement>
        <NumberInputField />
        <NumberInputIncrement>
          <Plus />
        </NumberInputIncrement>
      </NumberInputGroup>
    </NumberInput>
  );
}

Scrub Direction

import { Label } from "@/components/ui/label";
import {
  NumberInput,
  NumberInputField,
  NumberInputGroup,
  NumberInputScrubArea,
} from "@/components/ui/number-input";
 
export function NumberInputScrubDirection() {
  return (
    <NumberInput
      defaultValue={0}
      min={0}
      max={1000}
      className="max-w-32 flex-row items-center gap-2"
    >
      <NumberInputScrubArea direction="vertical">
        <Label className="cursor-ns-resize">Height</Label>
      </NumberInputScrubArea>
      <div className="bg-border h-7 w-px" />
      <NumberInputGroup>
        <NumberInputField className="rounded-md" />
      </NumberInputGroup>
    </NumberInput>
  );
}