Input
Examples
Default
Validation
Affixes & icons
prefix / suffix are outer affix segments. iconStart / iconEnd sit inside the field and follow reading direction in RTL.
Sizes
TanStack Form
Submit with an empty or invalid email to see field validation. A valid email clears the error and shows a success message.
RTL
Wrap the field (or a parent) in dir="rtl" / lang="fa". Outer prefix / suffix and iconStart / iconEnd follow the reading direction.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | "md" | Field height |
validation | "none" | "error" | "success" | "none" | Invalid (negative) / Valid (positive) |
prefix | ReactNode | — | Outer inline-start affix |
suffix | ReactNode | — | Outer inline-end affix |
iconStart | ReactNode | — | Icon inside the field at inline-start |
iconEnd | ReactNode | — | Icon inside the field at inline-end |
className | string | — | Class on the outer chrome |
inputClassName | string | — | Class on the native <input> |
disabled | boolean | — | Disabled state |
readOnly | boolean | — | Read-only state |
… | native input attrs | — | value, onChange, onBlur, name, placeholder, etc. |
Accessibility
WCAG success criteria
| Criterion | Level | How EDS helps | |
|---|---|---|---|
| 1.3.1 Info and Relationships | A | Native input preserves semantic relationships; consumer wires labels and aria-describedby for errors | |
| 3.3.1 Error Identification | A | validation="error" sets invalid visuals + aria-invalid="true"; consumer must expose error text (e.g. role="alert") | |
| 3.3.2 Labels or Instructions | A | Supports placeholder, native required, and all standard labeling attrs on the <input> | |
| 3.3.3 Error Suggestion | AA | Consumer: provide actionable error copy linked via aria-describedby | |
| 2.4.7 Focus Visible | AA | Token-based focus ring on :focus-within | |
| 1.4.3 Contrast (Minimum) | AA | Input text and borders use EDS color tokens | |
| 1.4.11 Non-text Contrast | AA | Border, focus ring, and validation indicators target 3:1 against adjacent colors | |
| 4.1.2 Name, Role, Value | A | Native <input> exposes name (via label/aria), role, value, and states (disabled, readonly, invalid) | |
| 2.1.1 Keyboard | A | Fully operable via keyboard through the native control |
Why it matters
Text inputs are the primary way users enter personal, financial, and operational data. If a field lacks a visible label, error association, or keyboard focus indicator, people who use screen readers, keyboards, or magnifiers cannot complete tasks — and everyone suffers when validation feedback is unclear. EDS keeps the native <input> as the focus target so form libraries, browser autofill, and assistive tech work without adapters.
Getting labeling or error wiring wrong is a compliance and product risk: users abandon forms, support tickets spike, and WCAG audits fail on basic field patterns.
Keyboard
The native <input> receives focus and handles all standard text-field keys. Chrome wrappers (prefix, icons, focus ring) are not in the tab order.
| Key / action | Behavior |
|---|---|
| ⇥ Tab | Moves focus to the <input> (skips decorative chrome) |
| ⇧ + ⇥ Tab | Moves focus to the previous focusable element |
| Type / ⌫ / Delete | Standard text entry and editing |
| ← → / Home / End | Move caret within the value |
| ↵ Enter | Submits the nearest <form> unless prevented |
| ⎋ Esc | Browser-default (e.g. clears autocomplete in some contexts) — no EDS override |
| State | Keyboard behavior |
|---|---|
| Default | Fully interactive |
disabled | Removed from tab order; not editable |
readOnly | Focusable and selectable; value cannot be changed |
validation="error" | Still fully editable — errors do not block typing |
validation="success" | Same as default; success icon is decorative |
Screen readers & semantics
| Element | Semantics |
|---|---|
<input> (ref target) | Native text field — exposes role, value, states to AT |
iconStart / iconEnd / success icon | Wrapped in aria-hidden="true" — decorative; do not convey meaning alone |
prefix / suffix affixes | Visual chrome only; not automatically linked to the input name |
Focus ring (eds-input__focus-ring) | aria-hidden="true" — visual only; focus is on the input |
| Concern | EDS / consumer responsibility |
|---|---|
| Accessible name | Consumer: associate <label htmlFor={id}>, or pass aria-label / aria-labelledby |
| Invalid state | EDS sets aria-invalid="true" when validation="error" (overridable via aria-invalid prop) |
| Error description | Consumer: link error text with aria-describedby={errorId} pointing to the error element |
required | Consumer: pass native required; consider aria-required if not using native constraint validation |
disabled / readOnly | Native attributes forwarded — AT announces state correctly |
forwardRef targets the <input> so programmatic focus (ref.current.focus()) and scroll-into-view from form libraries work as expected.
Focus & visuals
| Concern | EDS behavior |
|---|---|
| Focus visible | :focus-within on the chrome shows a token-based focus ring (--eds-input-focus-ring) around the field and affixes |
| Hover | Border emphasis on hover when not disabled, read-only, or focused |
| Validation colors | validation="error" applies negative border tokens; validation="success" applies positive border + optional check icon |
| Disabled | Muted visuals; no hover or focus ring treatment |
| RTL | prefix / suffix and iconStart / iconEnd use logical inline-start/end — layout mirrors under dir="rtl" |
| Contrast | Text and borders use EDS semantic tokens intended to meet WCAG AA on default surfaces |
The success checkmark is visual confirmation only — do not rely on it as the sole indicator of validity for screen-reader users; pair with text or programmatic state.
Build with it
| Do | Don’t | ||
|---|---|---|---|
Pair every field with a visible <label htmlFor={id}> or aria-label | Rely on placeholder as the only label | ||
Wire aria-describedby to error/helper text elements | Show red borders with no text explanation | ||
Set validation="error" and aria-invalid together in form bindings | Toggle only the border color without aria-invalid | ||
Mark decorative icons with aria-hidden (EDS does this for built-in icons) | Pass meaningful icons without a text alternative | ||
Use prefix/suffix for visual units ($, kg) when the label already explains the field | Let affixes replace proper labeling | ||
Forward name, onChange, onBlur for TanStack Form / native forms | Wrap the input in a custom component that breaks ref forwarding | ||
Use disabled for non-applicable fields; readOnly for display-with-copy | Use disabled for “locked after submit” if the value must still be submitted |
Validated field pattern:
const errorId = "email-error";
<>
<label htmlFor="email">Email</label>
<Input
id="email"
name="email"
validation={invalid ? "error" : "none"}
aria-invalid={invalid}
aria-describedby={invalid ? errorId : undefined}
/>
{invalid ? (
<p id={errorId} role="alert">
Enter a valid email address
</p>
) : null}
</>;See Usage → TanStack Form for a complete useForm binding example.
Quick test
- Keyboard-only — Tab to the input, type text, Shift+Tab away. Confirm a visible focus ring appears and the caret is in the field.
- Label — With VoiceOver/NVDA, focus the field. Confirm the accessible name matches the visible label (not just placeholder).
- Error state — Set
validation="error"with linked error text viaaria-describedby. Confirm AT reads the field as invalid and announces the error. - Disabled / read-only — Tab to each. Disabled should be skipped; read-only should focus but not accept edits.
- Icons & affixes — Add
iconStartandprefix. Confirm AT does not double-announce icon/affix content. - RTL — Wrap in
dir="rtl" lang="fa". Confirm affixes and icons flip to inline-start/end correctly. - Zoom — At 200% browser zoom, confirm the field, label, and error text remain visible without horizontal clipping.