Number Input
An input that returns a number value and automatically handles invalid inputs.
"use client"
import { NumberInput } from "@/components/ui/number-input"import { useState } from "react"
export function BasicNumberInput() { const [value, setValue] = useState<number | null>(null) return ( <NumberInput placeholder="Age" value={value} onChange={setValue} className="w-[250px]" /> )}Installation
Section titled “Installation”This installation provides support for all official Shadcn icon libraries. The component is otherwise identical to the non-experimental installation.
Icon support is experimental and may not be fully stable since it uses internal Shadcn APIs.
This component relies on other items which must be installed first.
Copy and paste the following code into your project.
components/ui/number-input.tsx
import { Input } from "@/components/ui/input"import { cn } from "@/lib/utils"import { type ComponentProps } from "react"
export function NumberInput({ onChange, value, ...props}: Omit<ComponentProps<typeof Input>, "type" | "onChange" | "value"> & { onChange: (value: number | null) => void value: undefined | null | number}) { return ( <Input {...props} onChange={e => { const number = e.target.valueAsNumber onChange(isNaN(number) ? null : number) }} value={value ?? ""} type="number" /> )}
export function InputGroupNumberInput({ className, ...props}: React.ComponentProps<typeof NumberInput>) { return ( <NumberInput data-slot="input-group-control" className={cn( "flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent", className, )} {...props} /> )}Update the import paths to match your project setup.
import { useState } from "react"import { NumberInput, InputGroupNumberInput } from "@/components/ui/NumberInput"const [value, setValue] = useState<number | null>(null)
<NumberInput value={value} onChange={setValue} />// Or<InputGroup> <InputGroupNumberInput value={value} onChange={setValue} /></InputGroup>Examples
Section titled “Examples”With Input Group
Section titled “With Input Group”"use client"
import { InputGroup, InputGroupAddon, InputGroupText,} from "@/components/ui/input-group"import { InputGroupNumberInput } from "@/components/ui/number-input"import { useState } from "react"
export function BasicNumberInput() { const [value, setValue] = useState<number | null>(null) return ( <InputGroup className="w-[300px]"> <InputGroupNumberInput placeholder="Favorite number" value={value} onChange={setValue} /> <InputGroupAddon> <InputGroupText>#</InputGroupText> </InputGroupAddon> </InputGroup> )}"use client"
import { zodResolver } from "@hookform/resolvers/zod"import { useForm } from "react-hook-form"import { toast } from "sonner"import { z } from "zod"import { Button } from "@/components/ui/button"import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage,} from "@/components/ui/form"import { NumberInput } from "@/components/ui/number-input"
const formSchema = z.object({ age: z.number().min(1), favoriteNumber: z.number().min(1).max(100).nullable(),})
export function NumberInputForm() { const form = useForm<z.infer<typeof formSchema>>({ resolver: zodResolver(formSchema), defaultValues: { favoriteNumber: null, }, })
function onSubmit(data: z.infer<typeof formSchema>) { toast("You submitted the following values", { description: ( <pre className="mt-2 w-[320px] rounded-md bg-neutral-950 p-4"> <code className="text-white">{JSON.stringify(data, null, 2)}</code> </pre> ), }) }
return ( <Form {...form}> <form onSubmit={form.handleSubmit(onSubmit)} className="w-2/3 space-y-6"> <FormField control={form.control} name="age" render={({ field }) => ( <FormItem> <FormLabel>Age</FormLabel> <FormControl> <NumberInput {...field} /> </FormControl> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="favoriteNumber" render={({ field }) => ( <FormItem> <FormLabel>Favorite Number</FormLabel> <FormControl> <NumberInput {...field} /> </FormControl> <FormMessage /> <FormDescription>Optional</FormDescription> </FormItem> )} /> <Button type="submit">Submit</Button> </form> </Form> )}