Input
Single-line text field. Supports labels, hints, error states, leading and trailing icons, and any native input type.
Installation
npx shadcn@latest add @wuko/inputUsage
import { Input } from "@/components/ui/input";
export function Example() {
return <Input label="Email" placeholder="you@example.com" />;
}Sizes
Three sizes mirror Button's sm / md / lg, so inputs and buttons line up cleanly when composed in the same row.
<Input size="sm" placeholder="Small" />
<Input size="md" placeholder="Medium" />
<Input size="lg" placeholder="Large" />With icons
Pass any ReactNode to leftIcon or rightIcon. Left icons are decorative. Pointer events are disabled so clicks fall through to the input. Right icons accept pointer events to support interactive controls (see Password below).
import { Mail, Search } from "lucide-react";
<Input
leftIcon={<Mail className="size-4" />}
placeholder="you@example.com"
/>
<Input
leftIcon={<Search className="size-4" />}
placeholder="Search docs..."
/>Hints & errors
hint renders supportive text below the field. error replaces it with an error message, swaps the border to --wuko-danger-fg, and sets aria-invalid="true". Visual and screen-reader signals are coupled. The styling is driven from the ARIA attribute, so you cannot show the error state without also announcing it.
Must be 3–20 characters.
Enter a valid email address.
<Input label="Username" defaultValue="wuko" hint="Must be 3–20 characters." />
<Input
label="Email"
defaultValue="not-an-email"
error="Enter a valid email address."
/>Password with toggle
Compose rightIcon with a clickable visibility toggle. This is the design intent behind the asymmetric icon slots. The right slot accepts pointer events so an embedded button can receive clicks.
Disabled
<Input label="Read-only" disabled defaultValue="wuko@wuko.dev" />Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | — | Visible field label, linked to the input via htmlFor. |
| hint | string | — | Helper text below the field. Linked to the input via aria-describedby. |
| error | string | — | Replaces hint with an error message, swaps the border to the destructive foreground token, and sets aria-invalid. |
| size | "sm" | "md" | "lg" | "md" | Controls height, font size, and horizontal padding. |
| leftIcon | ReactNode | — | Decorative icon inside the field on the left. Pointer events disabled so clicks fall through to the input. |
| rightIcon | ReactNode | — | Element inside the field on the right. Pointer events allowed. Supports interactive controls like a password reveal toggle. |
| type | string | "text" | Standard HTML input type. |
| disabled | boolean | false | Disables interaction and dims the field. |
| id | string | — | Optional. Defaults to a stable id generated via React.useId(). |
| ...rest | InputHTMLAttributes<HTMLInputElement> | — | All native input attributes are forwarded to the element. |
Accessibility
- Renders a native
<input>element. Tab focus, typing, and form submission work without extra wiring. - Each input gets a stable id via
React.useId(). The label is linked viahtmlFor; clicking the label focuses the input. Passidto override. errorsetsaria-invalid="true"; the visual error styling is driven from that ARIA attribute, so the screen-reader and visual signals are structurally coupled.- The hint or error message gets a unique id (suffixed
-msg). The input'saria-describedbypoints at it. If you pass your ownaria-describedby, Wuko merges yours with the message id rather than overwriting. - Focus uses
focus-visible:border-wuko-accentplus a softfocus-visible:ring-wuko-accent/30ring inside the visual frame, different convention from Button's externalfocus-visible:outline, because input fields conventionally show focus on the editable surface itself. disableduses the native attribute, removing the input from the tab order and blocking interaction. Style adjusts viadisabled:opacity-50anddisabled:cursor-not-allowed.- Left icons set
pointer-events-noneso they don't swallow clicks meant for the input. Right icons leave pointer events enabled, so an embedded button (e.g., password toggle) receives clicks. When using an interactive right-icon control, give the button a cleararia-label.