Getting Started
Components
- Accordion
- Alert
- Alert Dialog
- Avatar
- Badge
- Button
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Circular Progress
- Collapsible
- Compare Slider
- Dialog
- Dropdown Menu
- Gauge
- Input
- Label
- Marquee
- Navigation Menu
- Number Field
- Pagination
- Popover
- Progress
- Radio Group
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Switch
- Table
- Tabs
- Textarea
- Toggle
- Tooltip
Demos
A labelled text field.
"use client"
import { Input } from "@workspace/ui/components/input"
export function InputDemo() {
return (
<label className="grid w-full max-w-sm gap-1.5 text-xs font-medium">
Email
<Input type="email" placeholder="you@example.com" />
</label>
)
}
Usage
Use an input for short free text: a name, an email, a number. For long text, use a textarea. Every input needs a visible label.
Import it from @workspace/ui/components/input.
Anatomy
- Input — A styled
inputelement. All native attributes work.sizeissm,default, orlg.
Notes
- A placeholder is not a label. It disappears as soon as the reader types.
- Set the right
typeandinputModeso phones show the right keyboard. - Set
aria-invalidto show the error style. Put the error message right below the field. - Match
sizeto the button next to it. Both useh-8,h-10, andh-12. - Disabled fields are skipped by the keyboard. Use
readOnlyif the reader should still be able to copy the value.
Examples
Sizes
sm, default, and lg. Match the size of a button next to it.
"use client"
import { Input } from "@workspace/ui/components/input"
const sizes = ["sm", "default", "lg"] as const
export function InputSizes() {
return (
<div className="flex w-full max-w-sm flex-col gap-3">
{sizes.map((size) => (
<Input key={size} size={size} placeholder={size} aria-label={`Input, ${size}`} />
))}
</div>
)
}
Number
For numbers, such as weight or a dose, use a Number Field. It adds step buttons, arrow keys, limits, and number formatting.
File
Restrict the picker with accept.
"use client"
import { Input } from "@workspace/ui/components/input"
export function InputFile() {
return (
<label className="grid w-full max-w-sm gap-1.5 text-xs font-medium">
Upload lab report
<Input type="file" accept="application/pdf" />
</label>
)
}
Invalid
Set aria-invalid and show the error below the field.
Enter a 10 digit number.
"use client"
import { Input } from "@workspace/ui/components/input"
export function InputInvalid() {
return (
<div className="grid w-full max-w-sm gap-1.5 text-xs font-medium">
<label className="grid gap-1.5">
Phone
<Input
type="tel"
defaultValue="12345"
aria-invalid
aria-describedby="phone-error"
/>
</label>
<span id="phone-error" className="font-normal text-destructive">
Enter a 10 digit number.
</span>
</div>
)
}
Disabled
A field the reader can see but not change.
"use client"
import { Input } from "@workspace/ui/components/input"
export function InputDisabled() {
return (
<label className="grid w-full max-w-sm gap-1.5 text-xs font-medium">
Member ID
<Input defaultValue="PRD-20931" disabled />
</label>
)
}
With button
An input and its action in one row.
"use client"
import { Button } from "@workspace/ui/components/button"
import { Input } from "@workspace/ui/components/input"
export function InputWithButton() {
return (
<form className="flex w-full max-w-sm items-center gap-2">
<Input type="text" placeholder="Promo code" aria-label="Promo code" />
<Button type="submit">
Apply
</Button>
</form>
)
}
Props
These are the props you set most often. Unlisted props pass through to the input element.
Input
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm", "default", or "lg" | default | Height, padding, and text size. |
type | string | text | Native input type. |
placeholder | string | — | Hint shown when the field is empty. |
disabled | boolean | false | Blocks input and dims the field. |
readOnly | boolean | false | Keeps the value selectable but not editable. |
aria-invalid | boolean | — | Shows the error style. |
className | string | — | Classes merged onto the element. |