Input

A single-line hand-drawn text field with an optional label and error message.

Overview

A single-line text field for collecting a piece of data, with an optional visible label and (on React) an error message.

Anatomy

An optional label above a sketchy-outlined text box; on React, an error message renders below the box as a role="alert" element when error is set.

Variants & States

State Status Notes
Default Implemented
Focused Implemented Stroke color swaps to a focus token.
Disabled Implemented
Error / invalid Implemented React only — aria-invalid + aria-describedby + role="alert". Not implemented on Web Components or React Native.
Loading Not implemented

Web Components instead has required (native constraint validation via ElementInternals) — React and React Native don’t expose that as a prop at all. These are two separate, real gaps, not documentation omissions: don’t show an error demo for Web Components or React Native.

Usage

Do

  • Always pair error with a specific, actionable message ("Please enter at least 8 characters."), not a generic "Invalid input."
  • Keep label visible rather than relying on placeholder text alone.

Don't

  • Don't rely on placeholder text as the only accessible name — it disappears once the user types.
  • Don't show more than one error message per field.

Accessibility

Content

The label names the data being collected (“Email”, “Password”), not an action — contrast with Button, whose label names an action. Error messages state what’s wrong and how to fix it.

Props API

React@ghds/react

PropTypeDefaultDescription
labelstringVisible label, associated via Radix Label.
errorstringError message. When set, marks the field invalid and announces it.
disabledbooleanfalseDisables editing.
...restInputHTMLAttributesAll other native <input> attributes (value, onChange, etc.) pass through.

Web Components@ghds/web-components

PropTypeDefaultDescription
valuestring''Current field value, mirrored to the form value.
namestringSubmitted control name.
type'text' | 'email' | 'password' | 'search' | 'tel' | 'url' | 'number''text'Input mode.
placeholderstringPlaceholder text.
labelstringOptional visible label, also the accessible name.
disabledbooleanfalseDisables editing and form participation.
requiredbooleanfalseNative constraint validation. No error prop exists.

React Native@ghds/react-native

PropTypeDefaultDescription
valuestringControlled text value.
onChangeText(text: string) => voidChange handler.
placeholderstringPlaceholder; also the default a11y label.
disabledbooleanfalseDisables editing.
accessibilityLabelstringFalls back to placeholder.
keyboardTypeTextInputProps['keyboardType']Keyboard variant.
secureTextEntrybooleanMasks input for passwords. No error prop exists.
testIDstringTest handle for queries.

Code examples

// React — @ghds/react (onChange receives an event; `error` is React-only)
import { Input } from '@ghds/react/input';

<Input
  label="Email"
  placeholder="you@example.com"
  value={email}
  onChange={(event) => setEmail(event.target.value)}
/>;
<Input label="Password" type="password" error="Please enter at least 8 characters." />;
<!-- Web Components — @ghds/web-components (has `required`; no `error`/invalid prop) -->
<script type="module">
  import '@ghds/web-components/input';
</script>
<gh-input label="Email" placeholder="you@example.com" type="email"></gh-input>
<gh-input label="Username" required></gh-input>
// React Native — @ghds/react-native (onChangeText receives a bare string; no `error` prop)
import { Input } from '@ghds/react-native/input';

<Input
  value={email}
  onChangeText={setEmail}
  placeholder="you@example.com"
  keyboardType="email-address"
/>;

Live Demo

React’s demo includes the error state; the Web Components demo intentionally does not (it has no error prop) and shows required instead.

React @ghds/react

Web Components @ghds/web-components