Input
Single-line text field.
import { Field, FieldControl, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
<Field required>
<FieldLabel>Email</FieldLabel>
<FieldControl>
<Input type="email" placeholder="you@company.com" />
</FieldControl>
</Field>Examples
Example source
var P = window.PUI, h = React.createElement;
ReactDOM.createRoot(document.getElementById('preview')).render(h('div', { className: 'pui-row', style: { alignItems: 'flex-start' } },
h('div', { className: 'pui-field', style: { flex: '1 1 220px' } },
h(P.Label, { htmlFor: 'email' }, 'Email'),
h(P.Input, { id: 'email', type: 'email', placeholder: 'you@company.com' }),
h('span', { className: 'pui-muted' }, 'We send receipts here.')),
h(P.Field, { required: true, invalid: true, style: { flex: '1 1 220px' } },
h(P.FieldLabel, null, 'Username'),
h(P.FieldControl, null, h(P.Input, { defaultValue: 'maya' })),
h(P.FieldError, null, 'This username is taken. Try another one.'))));
Usage
What you provide
- A label, always: put the input inside
Field(or link aLabelthroughhtmlFor/id). A placeholder never replaces a label. - The right
type(email,tel,number,password) for mobile keyboards and autofill. - An optional
placeholderwith a real example ("you@company.com"), shown inmuted-foreground. - Errors:
aria-invalid="true"plus a message below intext-destructivethat says how to fix it.
Layout
- Label and field in
grid gap-2; fields stacked withgap-4. - Helper text below in
text-sm text-muted-foreground. - For validation, pair the fields with
react-hook-form+zod.
Accessibility
- Wrap the field in
Fieldso the label, instructions and error are tied to the input (aria-describedby,aria-invalid,required). The label stays visible after the field is filled; a placeholder is only an extra hint. - State instructions, required status and limits before the person types. Clicking the label focuses the field.
Tab/Shift+Tabfollow the page order; native editing, selection, copy and paste always work.Enterruns the form's submit action.- On an invalid value, keep what was typed and explain how to fix it ("Enter an email like name@example.com"), not "Invalid value". Don't show errors before the person had a chance to fill the field.
type="email": a valid format doesn't prove the address exists; accept one address unless the context needsmultiple.type="search": say whether search runs on submit or while typing; announce the number of results without moving focus; a "Clear search" control empties the field without submitting.- For passwords, use
PasswordInput.
Adapted from Magoo 1.6.0 by Marcelo Sales: Input "text" (input-text), Input "email" (input-email), Input "search" (input-search), Form validation (validacao-formularios). Magoo is educational guidance; test the behavior in your implementation.
API
Install with:
npx @aline-aroucha/pui add input label
<Field>
<FieldLabel />
<FieldControl>
<Input />
</FieldControl>
</Field>
Input
| Name | Type | Default |
|---|---|---|
type | "text" | "email" | "search" | "tel" | "url" | "number" | … | "text" |
| Native type; picks the right mobile keyboard. Use PasswordInput for passwords. | ||
value | string | — |
| Controlled value. | ||
defaultValue | string | — |
| Initial value when uncontrolled. | ||
placeholder | string | — |
| An example value; never a replacement for the label. | ||
disabled | boolean | false |
| Blocks editing. | ||
autoComplete | string | — |
| Autofill hint, e.g. "email". | ||
onChange | (event: ChangeEvent) => void | — |
| Runs on every edit. | ||