{
  "title": "InputGroup",
  "description": "Wrap a TextInput with fixed prefix and/or suffix slots that look visually integrated with the field — for things like @username, https:// addresses, or USD amounts.",
  "url": "/docs/components/input-group",
  "since": "0.3.0",
  "tags": [
    "input",
    "form"
  ],
  "platform": "both",
  "source": "---\ntitle: InputGroup\ndescription: Wrap a TextInput with fixed prefix and/or suffix slots that look visually integrated with the field — for things like @username, https:// addresses, or USD amounts.\nsince: 0.3.0\ntags: [input, form]\nplatform: both\ncategory: inputs\n---\n\nimport { BundleSize } from '@/components/bundle-size';\nimport { Preview } from '@/components/preview';\nimport { PropsTable } from '@/components/props-table';\n\n<BundleSize component=\"InputGroup\" />\n\n## At a glance\n\n`InputGroup` is a compound component that fuses a text field with one or two **non-interactive decorator slots** into a single rounded box. Inspired by Chakra's `InputGroup` and shadcn's input addon pattern.\n\n- One border around the whole compound — addons are not separate boxes.\n- Addons accept any `ReactNode`, so strings (`@`, `.com`, `USD`) and icons both work.\n- Clicking an addon focuses the input.\n- The whole group's border lights up when the input has focus (`focus-within` on web).\n- `InputGroupInput` extends the full `TextInput` API (`label`, `helperText`, `error`, `disabled`, …) so you don't lose anything by reaching for the integrated layout.\n- `disabled` and `error` on the group cascade visually to the addons.\n\n## Prefix\n\n<Preview name=\"input-group-prefix\" />\n\n## Suffix\n\n<Preview name=\"input-group-suffix\" />\n\n## Prefix and suffix\n\n<Preview name=\"input-group-both\" />\n\n## With label, helper text, and error\n\n`label`, `helperText`, and `error` belong on `<InputGroup.Input>`. They render *outside* the bordered field row — label above, helper/error below — exactly like a normal `TextInput`. When `error` is set, the entire group's border turns red and `aria-invalid` is forwarded to the `<input>`.\n\n```tsx\n<InputGroup>\n    <InputGroup.Addon>@</InputGroup.Addon>\n    <InputGroupInput\n        label=\"Username\"\n        helperText=\"3–20 characters, no spaces.\"\n        defaultValue=\"alice\"\n    />\n</InputGroup>\n```\n\n## Icon addons\n\nAddons accept any `ReactNode`, so you can drop in an icon component:\n\n```tsx\nimport { MailIcon } from 'lucide-react';\n\n<InputGroup>\n    <InputGroup.Addon><MailIcon size={16} /></InputGroup.Addon>\n    <InputGroup.Input label=\"Email\" placeholder=\"you@example.com\" />\n</InputGroup>\n```\n\n## `containerClassName`\n\nAdds Tailwind classes to the *outer wrapper* (label + field row +\nhelper text), as opposed to `className` which applies to the bordered\nfield row only. Use it when you need the entire labeled group to\nparticipate in a parent layout — e.g. setting a fixed width across a\nform.\n\n```tsx\n<InputGroup containerClassName=\"w-80\">\n    <InputGroup.Addon>@</InputGroup.Addon>\n    <InputGroup.Input label=\"Handle\" />\n</InputGroup>\n```\n\n## Disabled and error states\n\nPass `disabled` or `error` on the group itself to dim/mark the whole compound, or pass `disabled` / `error` on `<InputGroup.Input>` for input-only control. Either source flips the border + addon dim.\n\n```tsx\n<InputGroup disabled>\n    <InputGroup.Addon>@</InputGroup.Addon>\n    <InputGroup.Input defaultValue=\"locked\" />\n</InputGroup>\n\n<InputGroup>\n    <InputGroup.Addon>@</InputGroup.Addon>\n    <InputGroup.Input error=\"Invalid format\" defaultValue=\"not an email\" />\n</InputGroup>\n```\n\n## API\n\n### `<InputGroup>`\n\n<PropsTable component=\"InputGroup\" />\n\n### `<InputGroup.Addon>`\n\n<PropsTable component=\"InputGroupAddon\" />\n\n### `<InputGroup.Input>`\n\nInherits every prop from [`TextInput`](./text-input).\n"
}
