Single-line text field.

Code
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 a Label through htmlFor / id). A placeholder never replaces a label.
  • The right type (email, tel, number, password) for mobile keyboards and autofill.
  • An optional placeholder with a real example ("you@company.com"), shown in muted-foreground.
  • Errors: aria-invalid="true" plus a message below in text-destructive that says how to fix it.

Layout

  • Label and field in grid gap-2; fields stacked with gap-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 Field so 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+Tab follow the page order; native editing, selection, copy and paste always work. Enter runs 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 needs multiple.
  • 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

NameTypeDefault
type"text" | "email" | "search" | "tel" | "url" | "number" | …"text"
Native type; picks the right mobile keyboard. Use PasswordInput for passwords.
valuestring—
Controlled value.
defaultValuestring—
Initial value when uncontrolled.
placeholderstring—
An example value; never a replacement for the label.
disabledbooleanfalse
Blocks editing.
autoCompletestring—
Autofill hint, e.g. "email".
onChange(event: ChangeEvent) => void—
Runs on every edit.