Use a single-line text field for forms and data entry. Pair it with Field for labels and help text.
import { Input } from "@/components/ui/input"export function Example() { return <Input placeholder="Email" type="email" />}Installation
npx @arctis-sh/@arctis-sh/ui@latest add inputUsage
import { Input } from "@/components/ui/input"<Input />Field
Wrap the input with Field, FieldLabel, and FieldDescription to add a label and helper text.
Choose a unique username for your account.
import { Field, FieldDescription, FieldLabel,} from "@/components/ui/field"import { Input } from "@/components/ui/input"export function Example() { return ( <Field> <FieldLabel htmlFor="username">Username</FieldLabel> <FieldDescription> Choose a unique username for your account. </FieldDescription> <Input id="username" placeholder="your-username" /> </Field> )}Field Group
Use FieldGroup to stack related fields.
We'll send updates to this address.
import { Button } from "@/components/ui/button"import { Field, FieldDescription, FieldGroup, FieldLabel,} from "@/components/ui/field"import { Input } from "@/components/ui/input"export function Example() { return ( <FieldGroup> <Field> <FieldLabel htmlFor="name">Name</FieldLabel> <Input id="name" placeholder="Jane Doe" /> </Field> <Field> <FieldLabel htmlFor="email">Email</FieldLabel> <Input id="email" type="email" placeholder="jane@example.com" /> <FieldDescription> We'll send updates to this address. </FieldDescription> </Field> <div className="flex gap-2"> <Button type="button" variant="outline"> Reset </Button> <Button type="button">Submit</Button> </div> </FieldGroup> )}Disabled
Set disabled to prevent input.
import { Input } from "@/components/ui/input"export function Example() { return ( <div className="grid gap-2 opacity-60"> <label htmlFor="email-disabled" className="text-sm font-medium tracking-wide" > Email </label> <Input id="email-disabled" type="email" placeholder="Email" disabled /> </div> )}Invalid
Set aria-invalid when validation fails.
Enter a valid email address.
import { Input } from "@/components/ui/input"export function Example() { return ( <div className="grid gap-2"> <label htmlFor="email-invalid" className="text-sm font-medium tracking-wide text-destructive" > Email </label> <Input id="email-invalid" type="email" placeholder="Email" aria-invalid defaultValue="not-an-email" /> <p className="text-sm text-destructive"> Enter a valid email address. </p> </div> )}File
Use type="file" for uploads. Wrap it in a labeled control to keep Choose file on the left and the filename on the right. Browsers do not let you reposition the native “No file chosen” text with CSS alone.
Select a picture to upload.
"use client"import { useState } from "react"import { Input } from "@/components/ui/input"export function Example() { const [fileName, setFileName] = useState("No file chosen") return ( <div className="grid gap-2"> <label htmlFor="picture" className="text-sm font-medium tracking-wide" > Picture </label> <p className="text-sm text-muted-foreground"> Select a picture to upload. </p> <label htmlFor="picture" className="relative flex h-9 w-full cursor-pointer items-center overflow-hidden rounded-md border border-border bg-muted px-3 text-sm tracking-wide" > <span className="pointer-events-none font-medium">Choose file</span> <span className="pointer-events-none ml-auto truncate pl-3 text-muted-foreground"> {fileName} </span> <Input id="picture" type="file" className="absolute inset-0 cursor-pointer opacity-0" onChange={(event) => setFileName(event.target.files?.[0]?.name ?? "No file chosen") } /> </label> </div> )}Button
Place a button beside the input for search or submit.
import { Button } from "@/components/ui/button"import { Input } from "@/components/ui/input"export function Example() { return ( <div className="flex w-full items-center gap-2"> <Input type="search" placeholder="Search..." className="flex-1" /> <Button type="button"> Search </Button> </div> )}Grid
Lay out several inputs side by side with CSS grid.
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"import { Input } from "@/components/ui/input"export function Example() { return ( <FieldGroup className="grid gap-4 sm:grid-cols-2"> <Field> <FieldLabel htmlFor="first-name">First Name</FieldLabel> <Input id="first-name" placeholder="Alex" /> </Field> <Field> <FieldLabel htmlFor="last-name">Last Name</FieldLabel> <Input id="last-name" placeholder="Morgan" /> </Field> </FieldGroup> )}Badge
Add a Badge in the label for recommended or beta fields.
import { Badge } from "@/components/ui/badge"import { Field, FieldLabel } from "@/components/ui/field"import { Input } from "@/components/ui/input"export function Example() { return ( <Field> <FieldLabel htmlFor="webhook" className="w-full justify-between"> Webhook URL <Badge variant="secondary">Beta</Badge> </FieldLabel> <Input id="webhook" type="url" placeholder="https://example.com/webhook" /> </Field> )}Input Group
Use Input Group for icons, text, or buttons inside the field.
import { Field, FieldLabel } from "@/components/ui/field"import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText,} from "@/components/ui/input-group"export function Example() { return ( <Field> <FieldLabel htmlFor="website">Website URL</FieldLabel> <InputGroup variant="filled"> <InputGroupAddon> <InputGroupText>https://</InputGroupText> </InputGroupAddon> <InputGroupInput id="website" placeholder="example.com" /> </InputGroup> </Field> )}Button Group
Use Button Group to attach buttons beside the field.
import { Button, ButtonGroup } from "@/components/ui/button"import { buttonGroupItemClass, useButtonGroup,} from "@/components/ui/button-group"import { cn } from "@/lib/utils"function SearchIcon(props: React.SVGProps<SVGSVGElement>) { return ( <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" {...props}> <circle cx="11" cy="11" r="8" /> <path d="m21 21-4.3-4.3" /> </svg> )}function GroupInput({ className, ...props}: React.ComponentProps<"input">) { const group = useButtonGroup() return ( <input data-slot="button-group-input" className={cn( "h-9 min-w-0 flex-1 border border-border bg-transparent px-3 text-sm text-foreground outline-none placeholder:text-muted-foreground", buttonGroupItemClass(group), className, )} {...props} /> )}export function Example() { return ( <ButtonGroup className="w-full max-w-sm" aria-label="Search"> <GroupInput placeholder="Search…" /> <Button variant="outline" size="icon" aria-label="Search"> <SearchIcon /> </Button> </ButtonGroup> )}API Reference
Input
| Prop | Type | Default |
|---|---|---|
| type | string | "text" |
| placeholder | string | — |
| disabled | boolean | false |
| aria-invalid | boolean | — |
| className | string | — |
Also accepts the other native input attributes (id, name, value, defaultValue, onChange, and so on).
<Input type="email" placeholder="you@example.com" /><Input type="file" />