InputGroup

A hand-drawn field that composes a bare input with leading and trailing addons in one sketchy box.

Overview

InputGroup composes a bare text input with one or more leading and/or trailing addons — icons, short text, or buttons — inside a single hand-drawn box. Use it for fields that need an affordance attached to the input: a https:// prefix, a currency unit, a search icon, or a submit button.

Anatomy

A flex row painting one sketchy SketchSurface box around its children:

Focus anywhere inside the group switches the box outline to the focus stroke. Every color, padding, radius, and sketch parameter comes from @ghds/tokens (comp.inputGroup.*).

Variants & States

There are no named variants — instead the group carries disabled and invalid booleans, and it reacts to focus.

State Status Notes
Focus Implemented Focus within the group switches the outline to comp.inputGroup.stroke.focus.
Invalid Implemented The invalid prop paints the danger stroke.
Disabled Implemented Disables the input and mutes the fill.
Hover Not implemented
Loading Not implemented
Error Implemented Same as invalid — there is no separate error state.

Usage

Do

  • Place the addon before or after InputGroupInput in child order to make it lead or trail.
  • Set invalid together with an external error message so the reason is explained, not just colored.
  • Give InputGroupInput an aria-label (or an associated <label>) — the group is not a labeled field by itself.

Don't

  • Don't rely on the danger stroke alone to convey an error — pair it with text describing the problem.
  • Don't put more than one InputGroupInput in a group; it is one field with addons, not a multi-input row.

Accessibility

Content

Keep addons short — a prefix (https://, @), a unit (USD, / mo), an icon, or a single small button. Long addon text competes with the input value for space.

Props API

React@ghds/react

PropTypeDefaultDescription
disabledbooleanfalseDisables the whole group and mutes the surface (InputGroup).
invalidbooleanfalseMarks the group invalid, painting the danger stroke (InputGroup).
...restHTMLAttributes<HTMLDivElement>Native <div> attributes pass through to the group container.
InputGroupInputInputHTMLAttributes (minus 'size')The bare inner <input>; accepts native input attributes.
InputGroupAddonHTMLAttributes<HTMLSpanElement>A leading/trailing addon <span>; position set by child order.

Web Components@ghds/web-components

PropTypeDefaultDescription
disabledbooleanfalseReflected attribute; disables the group and mutes the surface.
invalidbooleanfalseReflected attribute; paints the danger stroke.
slot(default)Slot addons (text/icons) and a bare <input> in child order; a slotted <input> loses its own chrome and the group paints the box.

React Native@ghds/react-native

PropTypeDefaultDescription
disabledbooleanfalseDisables the whole group and mutes the surface (InputGroup).
invalidbooleanfalseMarks the group invalid, painting the danger stroke (InputGroup).
InputGroupInputTextInputProps (minus editable/onFocus/onBlur) + disabledThe bare inner TextInput; disabled is inherited from the group unless set.
InputGroupAddon{ children }A leading/trailing addon; a string child renders as muted addon text.
testIDstringTest handle for queries (InputGroup).

Code examples

// React — @ghds/react (addon position set by child order)
import { InputGroup, InputGroupAddon, InputGroupInput } from '@ghds/react/input-group';

<InputGroup>
  <InputGroupAddon>https://</InputGroupAddon>
  <InputGroupInput placeholder="example.com" aria-label="Website" />
</InputGroup>;
<!-- Web Components — @ghds/web-components (slot a bare <input>; the group paints the box) -->
<script type="module">
  import '@ghds/web-components/input-group';
</script>
<gh-input-group>
  <span>https://</span>
  <input aria-label="Website" placeholder="example.com" />
</gh-input-group>
// React Native — @ghds/react-native
import { InputGroup, InputGroupInput, InputGroupAddon } from '@ghds/react-native/input-group';

<InputGroup>
  <InputGroupAddon>https://</InputGroupAddon>
  <InputGroupInput placeholder="example.com" accessibilityLabel="Website" />
</InputGroup>;

Live Demo

React and Web Components render live below, driven by the same tokens — toggle dark mode in the header to see both respond identically. React Native can’t run in a browser without additional react-native-web wiring (not set up in this site), so its usage is shown as a code sample instead.

React @ghds/react

Web Components @ghds/web-components

https:// USD @ https://