# InputGroup primitives (/components/primitives/input-group)



Use these primitives to build a custom text control. Reach for them when you need the styled input
without label, description, or error slots. For most application forms, use
[`TextField`](/components/forms/text-field).

apps/docs/src/examples/input-group-primitive/basic.tsx

```tsx
import {
	InputGroup,
	InputGroupInput,
	InputGroupPrefix,
	InputGroupSuffix,
} from '@luke-ui/react/primitives/input-group';

export default () => {
	return (
		<InputGroup>
			<InputGroupPrefix>$</InputGroupPrefix>
			<InputGroupInput aria-label="Amount" inputMode="decimal" placeholder="0.00" />
			<InputGroupSuffix>USD</InputGroupSuffix>
		</InputGroup>
	);
};
```

## Anatomy [#anatomy]

`InputGroup` owns the border, background, shadow, and rounding. Its parts are transparent flex
children, so position follows document order. Put `InputGroupPrefix` before `InputGroupInput` and
`InputGroupSuffix` after it.

```tsx
<InputGroup>
	<InputGroupPrefix>$</InputGroupPrefix>
	<InputGroupInput aria-label="Amount" inputMode="decimal" />
	<InputGroupSuffix>USD</InputGroupSuffix>
</InputGroup>
```

A prefix or suffix accepts any React node, including an interactive one such as a button. Give an
interactive part an accessible name, keyboard behaviour, and a considered focus order.

## Size [#size]

Set `size` on `InputGroup` and the parts inherit it. `medium` is the default. Use `small` when the
input belongs in a compact control group. Match the size of nearby fields and actions. An individual
part can override the inherited size.

The group also sets the icon size for everything inside it, so an `Icon` in a prefix or suffix
scales with the control without a `size` of its own.

## Validation [#validation]

`InputGroup` reads its invalid state from the enclosing React Aria field, or from an explicit
`isInvalid`. While invalid it renders an error icon after the input value and before any
`InputGroupSuffix`.

The icon is decorative and hidden from assistive technology, so it marks a problem but does not
describe it. Always give an invalid composition an error message. See
[Validation](/docs/validation).

## Styling and composition [#styling-and-composition]

`className` on `InputGroup` applies to the group element, and `className` on `InputGroupInput`
applies to the input. Use these hooks when you compose the primitive with other controls. Give a
standalone `InputGroupInput` an accessible name with `aria-label` or a connected label.

## API [#api]

### InputGroupProps [#inputgroupprops]

<ComponentPropsTable
  id="type-table-input-group.tsx-InputGroupProps"
  type="{
  &#x22;id&#x22;: &#x22;input-group.tsx-InputGroupProps&#x22;,
  &#x22;name&#x22;: &#x22;InputGroupProps&#x22;,
  &#x22;description&#x22;: &#x22;Props for the input group root.&#x22;,
  &#x22;entries&#x22;: [
    {
      &#x22;name&#x22;: &#x22;children&#x22;,
      &#x22;description&#x22;: &#x22;The group's parts. Position follows document order.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;ChildrenOrFunction<GroupRenderProps>&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;ChildrenOrFunction<object>&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;className&#x22;,
      &#x22;description&#x22;: &#x22;Class name for the group element.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;ClassNameOrFunction<GroupRenderProps> | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;union&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;isInvalid&#x22;,
      &#x22;description&#x22;: &#x22;Whether the control is invalid. Inherited from an enclosing field when omitted.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;boolean | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;union&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;size&#x22;,
      &#x22;description&#x22;: &#x22;Sets the control size. Nested parts inherit it.&#x22;,
      &#x22;tags&#x22;: [
        {
          &#x22;name&#x22;: &#x22;default&#x22;,
          &#x22;text&#x22;: &#x22;'medium'&#x22;
        }
      ],
      &#x22;type&#x22;: &#x22;\&#x22;medium\&#x22; | \&#x22;small\&#x22; | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;union&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;render&#x22;,
      &#x22;description&#x22;: &#x22;Overrides the default DOM element with a custom render function.\nThis allows rendering existing components with built-in styles and behaviors\nsuch as router links, animation libraries, and pre-styled components.\n\nRequirements:\n\n- You must render the expected element type (e.g. if `<button>` is expected, you cannot render an\n  `<a>`).\n- Only a single root DOM element can be rendered (no fragments).\n- You must pass through props and ref to the underlying DOM element, merging with your own prop\n  as appropriate.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;DOMRenderFunction<\&#x22;div\&#x22;, GroupRenderProps> | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;DOMRenderFunction<\&#x22;div\&#x22;, object>&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;slot&#x22;,
      &#x22;description&#x22;: &#x22;A slot name for the component. Slots allow the component to receive props from a parent\ncomponent. An explicit `null` value indicates that the local props completely override all\nprops received from a parent.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string | null | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;union&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;role&#x22;,
      &#x22;description&#x22;: &#x22;An accessibility role for the group. By default, this is set to `'group'`.\nUse `'region'` when the contents of the group is important enough to be\nincluded in the page table of contents. Use `'presentation'` if the group\nis visual only and does not represent a semantic grouping of controls.&#x22;,
      &#x22;tags&#x22;: [
        {
          &#x22;name&#x22;: &#x22;default&#x22;,
          &#x22;text&#x22;: &#x22;'group'&#x22;
        }
      ],
      &#x22;type&#x22;: &#x22;\&#x22;group\&#x22; | \&#x22;presentation\&#x22; | \&#x22;region\&#x22; | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;union&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;aria-describedby&#x22;,
      &#x22;description&#x22;: &#x22;Identifies the element (or elements) that describes the object.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;aria-details&#x22;,
      &#x22;description&#x22;: &#x22;Identifies the element (or elements) that provide a detailed, extended description for the\nobject.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;aria-label&#x22;,
      &#x22;description&#x22;: &#x22;Defines a string value that labels the current element.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;aria-labelledby&#x22;,
      &#x22;description&#x22;: &#x22;Identifies the element (or elements) that labels the current element.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;isDisabled&#x22;,
      &#x22;description&#x22;: &#x22;Whether the group is disabled.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;boolean | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;union&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;isReadOnly&#x22;,
      &#x22;description&#x22;: &#x22;Whether the group is read only.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;boolean | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;union&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;onHoverStart&#x22;,
      &#x22;description&#x22;: &#x22;Handler that is called when a hover interaction starts.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;((e: HoverEvent) => void) | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;function&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;onHoverEnd&#x22;,
      &#x22;description&#x22;: &#x22;Handler that is called when a hover interaction ends.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;((e: HoverEvent) => void) | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;function&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;onHoverChange&#x22;,
      &#x22;description&#x22;: &#x22;Handler that is called when the hover state changes.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;((isHovering: boolean) => void) | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;function&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;deprecated&#x22;: false,
      &#x22;description&#x22;: &#x22;`InputGroupProps` also accepts compatible DOM and ARIA attributes and event handlers for its rendered element.&#x22;,
      &#x22;name&#x22;: &#x22;__nativePropsForwarding&#x22;,
      &#x22;required&#x22;: true,
      &#x22;simplifiedType&#x22;: &#x22;&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;&#x22;
    }
  ]
}"
/>

### InputGroupInputProps [#inputgroupinputprops]

<ComponentPropsTable
  id="type-table-input-group.tsx-InputGroupInputProps"
  type="{
  &#x22;id&#x22;: &#x22;input-group.tsx-InputGroupInputProps&#x22;,
  &#x22;name&#x22;: &#x22;InputGroupInputProps&#x22;,
  &#x22;description&#x22;: &#x22;Props for the input group's editable control.&#x22;,
  &#x22;entries&#x22;: [
    {
      &#x22;name&#x22;: &#x22;aria-label&#x22;,
      &#x22;description&#x22;: &#x22;Accessible name for the input when no visible label is connected.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;className&#x22;,
      &#x22;description&#x22;: &#x22;Class name for the input element.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;ClassNameOrFunction<InputRenderProps> | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;union&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;inputMode&#x22;,
      &#x22;description&#x22;: &#x22;Hints which input mechanism is most appropriate for the entered content.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;\&#x22;none\&#x22; | \&#x22;text\&#x22; | \&#x22;search\&#x22; | \&#x22;tel\&#x22; | \&#x22;url\&#x22; | \&#x22;email\&#x22; | \&#x22;numeric\&#x22; | \&#x22;decimal\&#x22; | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;union&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;ref&#x22;,
      &#x22;description&#x22;: &#x22;Forwarded to the underlying `<input>` element. Accepts a callback ref or a ref\nobject, so form libraries that hand out callback refs work without a bridge.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;Ref<HTMLInputElement> | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;union&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;render&#x22;,
      &#x22;description&#x22;: &#x22;Overrides the default DOM element with a custom render function.\nThis allows rendering existing components with built-in styles and behaviors\nsuch as router links, animation libraries, and pre-styled components.\n\nRequirements:\n\n- You must render the expected element type (e.g. if `<button>` is expected, you cannot render an\n  `<a>`).\n- Only a single root DOM element can be rendered (no fragments).\n- You must pass through props and ref to the underlying DOM element, merging with your own prop\n  as appropriate.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;DOMRenderFunction<\&#x22;input\&#x22;, InputRenderProps> | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;DOMRenderFunction<\&#x22;input\&#x22;, object>&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;onHoverStart&#x22;,
      &#x22;description&#x22;: &#x22;Handler that is called when a hover interaction starts.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;((e: HoverEvent) => void) | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;function&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;onHoverEnd&#x22;,
      &#x22;description&#x22;: &#x22;Handler that is called when a hover interaction ends.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;((e: HoverEvent) => void) | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;function&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;onHoverChange&#x22;,
      &#x22;description&#x22;: &#x22;Handler that is called when the hover state changes.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;((isHovering: boolean) => void) | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;function&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;placeholder&#x22;,
      &#x22;description&#x22;: &#x22;Temporary text that occupies the text input when it is empty.\nSee [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/placeholder).&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;size&#x22;,
      &#x22;description&#x22;: &#x22;Overrides the size inherited from the enclosing `InputGroup`.&#x22;,
      &#x22;tags&#x22;: [
        {
          &#x22;name&#x22;: &#x22;default&#x22;,
          &#x22;text&#x22;: &#x22;'medium'&#x22;
        }
      ],
      &#x22;type&#x22;: &#x22;\&#x22;medium\&#x22; | \&#x22;small\&#x22; | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;union&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;deprecated&#x22;: false,
      &#x22;description&#x22;: &#x22;`InputGroupInputProps` also accepts compatible DOM and ARIA attributes and event handlers for its rendered element.&#x22;,
      &#x22;name&#x22;: &#x22;__nativePropsForwarding&#x22;,
      &#x22;required&#x22;: true,
      &#x22;simplifiedType&#x22;: &#x22;&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;&#x22;
    }
  ]
}"
/>

### InputGroupPrefixProps [#inputgroupprefixprops]

<ComponentPropsTable
  id="type-table-input-group.tsx-InputGroupPrefixProps"
  type="{
  &#x22;id&#x22;: &#x22;input-group.tsx-InputGroupPrefixProps&#x22;,
  &#x22;name&#x22;: &#x22;InputGroupPrefixProps&#x22;,
  &#x22;description&#x22;: &#x22;Props for the input group's leading part.&#x22;,
  &#x22;entries&#x22;: [
    {
      &#x22;name&#x22;: &#x22;size&#x22;,
      &#x22;description&#x22;: &#x22;Overrides the size inherited from the enclosing `InputGroup`.&#x22;,
      &#x22;tags&#x22;: [
        {
          &#x22;name&#x22;: &#x22;default&#x22;,
          &#x22;text&#x22;: &#x22;'medium'&#x22;
        }
      ],
      &#x22;type&#x22;: &#x22;\&#x22;medium\&#x22; | \&#x22;small\&#x22; | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;union&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;deprecated&#x22;: false,
      &#x22;description&#x22;: &#x22;`InputGroupPrefixProps` also accepts compatible DOM and ARIA attributes and event handlers for its rendered element.&#x22;,
      &#x22;name&#x22;: &#x22;__nativePropsForwarding&#x22;,
      &#x22;required&#x22;: true,
      &#x22;simplifiedType&#x22;: &#x22;&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;&#x22;
    }
  ]
}"
/>

### InputGroupSuffixProps [#inputgroupsuffixprops]

<ComponentPropsTable
  id="type-table-input-group.tsx-InputGroupSuffixProps"
  type="{
  &#x22;id&#x22;: &#x22;input-group.tsx-InputGroupSuffixProps&#x22;,
  &#x22;name&#x22;: &#x22;InputGroupSuffixProps&#x22;,
  &#x22;description&#x22;: &#x22;Props for the input group's trailing part.&#x22;,
  &#x22;entries&#x22;: [
    {
      &#x22;name&#x22;: &#x22;size&#x22;,
      &#x22;description&#x22;: &#x22;Overrides the size inherited from the enclosing `InputGroup`.&#x22;,
      &#x22;tags&#x22;: [
        {
          &#x22;name&#x22;: &#x22;default&#x22;,
          &#x22;text&#x22;: &#x22;'medium'&#x22;
        }
      ],
      &#x22;type&#x22;: &#x22;\&#x22;medium\&#x22; | \&#x22;small\&#x22; | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;union&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;deprecated&#x22;: false,
      &#x22;description&#x22;: &#x22;`InputGroupSuffixProps` also accepts compatible DOM and ARIA attributes and event handlers for its rendered element.&#x22;,
      &#x22;name&#x22;: &#x22;__nativePropsForwarding&#x22;,
      &#x22;required&#x22;: true,
      &#x22;simplifiedType&#x22;: &#x22;&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;&#x22;
    }
  ]
}"
/>
