Input
Text input with focus ring, disabled state, and file input styling.
Examples
File input
type="file" is styled to match the rest of the form controls.
Select a picture to upload.
import { Field, FieldDescription, FieldLabel } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
export function InputFile() {
return (
<Field>
<FieldLabel htmlFor="picture">Picture</FieldLabel>
<Input id="picture" type="file" />
<FieldDescription>Select a picture to upload.</FieldDescription>
</Field>
);
}
Badge
Use Badge in the label to highlight a recommended field.
import { Badge } from "@codefast/ui/badge";
import { Field, FieldLabel } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
export function InputBadge() {
return (
<Field>
<FieldLabel htmlFor="input-badge">
Webhook URL{" "}
<Badge variant="secondary" className="ms-auto">
Beta
</Badge>
</FieldLabel>
<Input id="input-badge" type="url" placeholder="https://api.example.com/webhook" />
</Field>
);
}
Basic
A text input component for forms and user data entry with built-in styling and accessibility features.
import { Input } from "@codefast/ui/input";
export function InputBasic() {
return <Input placeholder="Enter text" />;
}
Disabled
Use the disabled prop to disable the input. To style the disabled state, add the data-disabled attribute to the Field component.
This field is currently disabled.
import { Field, FieldDescription, FieldLabel } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
export function InputDisabled() {
return (
<Field data-disabled>
<FieldLabel htmlFor="input-demo-disabled">Email</FieldLabel>
<Input id="input-demo-disabled" type="email" placeholder="Email" disabled />
<FieldDescription>This field is currently disabled.</FieldDescription>
</Field>
);
}
Field
Use Field, FieldLabel, and FieldDescription to create an input with a label and description.
Choose a unique username for your account.
import { Field, FieldDescription, FieldLabel } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
export function InputField() {
return (
<Field>
<FieldLabel htmlFor="input-field-username">Username</FieldLabel>
<Input id="input-field-username" type="text" placeholder="Enter your username" />
<FieldDescription>Choose a unique username for your account.</FieldDescription>
</Field>
);
}
Field Group
Use FieldGroup to show multiple Field blocks and to build forms.
We'll send updates to this address.
import { Button } from "@codefast/ui/button";
import { Field, FieldDescription, FieldGroup, FieldLabel } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
export function InputFieldgroup() {
return (
<FieldGroup>
<Field>
<FieldLabel htmlFor="fieldgroup-name">Name</FieldLabel>
<Input id="fieldgroup-name" placeholder="Jordan Lee" />
</Field>
<Field>
<FieldLabel htmlFor="fieldgroup-email">Email</FieldLabel>
<Input id="fieldgroup-email" type="email" placeholder="name@example.com" />
<FieldDescription>We'll send updates to this address.</FieldDescription>
</Field>
<Field orientation="horizontal">
<Button type="reset" variant="outline">
Reset
</Button>
<Button type="submit">Submit</Button>
</Field>
</FieldGroup>
);
}
Form
A full form example with multiple inputs, a select, and a button.
import { Button } from "@codefast/ui/button";
import { Field, FieldDescription, FieldGroup, FieldLabel } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@codefast/ui/select";
export function InputForm() {
return (
<form className="w-full max-w-sm">
<FieldGroup>
<Field>
<FieldLabel htmlFor="form-name">Name</FieldLabel>
<Input id="form-name" type="text" placeholder="Ava Stone" required />
</Field>
<Field>
<FieldLabel htmlFor="form-email">Email</FieldLabel>
<Input id="form-email" type="email" placeholder="john@example.com" />
<FieldDescription>We'll never share your email with anyone.</FieldDescription>
</Field>
<div className="grid grid-cols-2 gap-4">
<Field>
<FieldLabel htmlFor="form-phone">Phone</FieldLabel>
<Input id="form-phone" type="tel" placeholder="+1 (555) 123-4567" />
</Field>
<Field>
<FieldLabel htmlFor="form-country">Country</FieldLabel>
<Select defaultValue="us">
<SelectTrigger id="form-country">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="us">United States</SelectItem>
<SelectItem value="uk">United Kingdom</SelectItem>
<SelectItem value="ca">Canada</SelectItem>
</SelectContent>
</Select>
</Field>
</div>
<Field>
<FieldLabel htmlFor="form-address">Address</FieldLabel>
<Input id="form-address" type="text" placeholder="123 Main St" />
</Field>
<Field orientation="horizontal">
<Button type="button" variant="outline">
Cancel
</Button>
<Button type="submit">Submit</Button>
</Field>
</FieldGroup>
</form>
);
}
Grid
Use a grid layout to place multiple inputs side by side.
import { Field, FieldGroup, FieldLabel } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
export function InputGrid() {
return (
<FieldGroup className="grid max-w-sm grid-cols-2">
<Field>
<FieldLabel htmlFor="first-name">First Name</FieldLabel>
<Input id="first-name" placeholder="Jordan" />
</Field>
<Field>
<FieldLabel htmlFor="last-name">Last Name</FieldLabel>
<Input id="last-name" placeholder="Lee" />
</Field>
</FieldGroup>
);
}
Inline
Use Field with orientation='horizontal' to create an inline input. Pair with Button to create a search input with a button.
import { Button } from "@codefast/ui/button";
import { Field } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
export function InputInline() {
return (
<Field orientation="horizontal">
<Input type="search" placeholder="Search..." />
<Button>Search</Button>
</Field>
);
}
Input Group
To add icons, text, or buttons inside an input, use the InputGroup component. See the Input Group component for more examples.
import { Field, FieldLabel } from "@codefast/ui/field";
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from "@codefast/ui/input-group";
import { InfoIcon } from "lucide-react";
export function InputInputGroup() {
return (
<Field>
<FieldLabel htmlFor="input-group-url">Website URL</FieldLabel>
<InputGroup>
<InputGroupInput id="input-group-url" placeholder="example.com" />
<InputGroupAddon>
<InputGroupText>https://</InputGroupText>
</InputGroupAddon>
<InputGroupAddon align="inline-end">
<InfoIcon />
</InputGroupAddon>
</InputGroup>
</Field>
);
}
Invalid
Use the aria-invalid prop to mark the input as invalid. To style the invalid state, add the data-invalid attribute to the Field component.
This field contains validation errors.
import { Field, FieldDescription, FieldLabel } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
export function InputInvalid() {
return (
<Field data-invalid>
<FieldLabel htmlFor="input-invalid">Invalid Input</FieldLabel>
<Input id="input-invalid" placeholder="Error" aria-invalid />
<FieldDescription>This field contains validation errors.</FieldDescription>
</Field>
);
}
Required
Use the required attribute to indicate required inputs.
This field must be filled out.
import { Field, FieldDescription, FieldLabel } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
export function InputRequired() {
return (
<Field>
<FieldLabel htmlFor="input-required">
Required Field <span className="text-destructive">*</span>
</FieldLabel>
<Input id="input-required" placeholder="This field is required" required />
<FieldDescription>This field must be filled out.</FieldDescription>
</Field>
);
}
RTL
Right-to-left layout support for languages such as Arabic and Hebrew.
Translations are AI-generated for demonstration and may be imperfect.
مفتاح API الخاص بك مشفر ومخزن بأمان.
import { Field, FieldDescription, FieldLabel } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
import type { Translations } from "#/features/components-catalog/components/detail/language";
import { useTranslation } from "#/features/components-catalog/components/detail/language-context";
const translations: Translations = {
en: {
dir: "ltr",
values: {
apiKey: "API Key",
placeholder: "sk-...",
description: "Your API key is encrypted and stored securely.",
},
},
ar: {
dir: "rtl",
values: {
apiKey: "مفتاح API",
placeholder: "sk-...",
description: "مفتاح API الخاص بك مشفر ومخزن بأمان.",
},
},
he: {
dir: "rtl",
values: {
apiKey: "מפתח API",
placeholder: "sk-...",
description: "מפתח ה-API שלך מוצפן ונשמר בצורה מאובטחת.",
},
},
};
export function InputRtl() {
const { dir, t } = useTranslation(translations, "ar");
return (
<Field dir={dir}>
<FieldLabel htmlFor="input-rtl-api-key">{t.apiKey}</FieldLabel>
<Input id="input-rtl-api-key" type="password" placeholder={t.placeholder} dir={dir} />
<FieldDescription>{t.description}</FieldDescription>
</Field>
);
}
Anatomy
How the parts nest — every slot the component exposes, in composition order.
Features
- Forwards every native <input> prop and type (text, email, password, number, …), with type="file" getting dedicated styling.
- Built-in invalid (aria-invalid) and disabled states, styled without extra classes.
- Composes with Field/FieldGroup for a labelled field, InputGroup for icons or buttons inside the field, and ButtonGroup for adjacent action buttons.
API reference
Props for each part of the component. All native element props are also forwarded.
Input
Renders a native <input>; forwards every native input prop.
type"text" | "email" | "password" | "file" | "number" | …Native input type. file gets dedicated styling.
Default
"text"disabledbooleanDims the field and blocks interaction.
Default
falsearia-invalidbooleanSwitches the field to the destructive error ring.
Default
false
Accessibility
Built to be keyboard-navigable and screen-reader friendly out of the box.
| Key | Function |
|---|---|
| Tab | Moves focus to and from the input. |
- Always associate a Label via htmlFor / id so the field has an accessible name.
- Use aria-invalid plus a visible error message — colour alone is not sufficient.
- Describe the field with aria-describedby when you show helper or error text.
Guidelines
Conventions that keep usage consistent across an app.
Do
- Give every input a visible label.
- Use the correct type so mobile keyboards and autofill behave well.
Don’t
- Don’t use placeholder text as a replacement for a label.
- Don’t disable a field when a read-only state would communicate intent better.
Explore further
Ready to integrate?
Follow the Getting Started guide to install @codefast/ui, or browse the full component gallery.