Skip to main contentSkip to docs navigation

Input Group

Extend form controls by adding text, buttons, or button groups on either side of textual inputs, custom selects, and custom file inputs.

Basic usage

Place one add-on or button on either side of an input, or on both sides at once. Place <FormLabel>s outside the input group, rather than inside it.

@
@example.com
https://example.com/users/
$.00
@
With textarea
JSX
import { FormLabel, InputGroup, InputGroupAddon, TextInput, Textarea } from '@chassis-ui/react'

export const Example = () => {
  return (
    <>
      <InputGroup className="mb-md">
        <InputGroupAddon id="basic-addon1">@</InputGroupAddon>
        <TextInput placeholder="Username" aria-label="Username" aria-describedby="basic-addon1" />
      </InputGroup>

      <InputGroup className="mb-md">
        <TextInput
          placeholder="Recipient's username"
          aria-label="Recipient's username"
          aria-describedby="basic-addon2"
        />
        <InputGroupAddon id="basic-addon2">@example.com</InputGroupAddon>
      </InputGroup>

      <FormLabel htmlFor="basic-url" id="basic-url-label">
        Your vanity URL
      </FormLabel>
      <InputGroup className="mb-md">
        <InputGroupAddon id="basic-addon3">https://example.com/users/</InputGroupAddon>
        <TextInput
          id="basic-url"
          aria-labelledby="basic-url-label"
          aria-describedby="basic-addon3"
        />
      </InputGroup>

      <InputGroup className="mb-md">
        <InputGroupAddon>$</InputGroupAddon>
        <TextInput aria-label="Amount (to the nearest dollar)" />
        <InputGroupAddon>.00</InputGroupAddon>
      </InputGroup>

      <InputGroup className="mb-md">
        <TextInput placeholder="Username" aria-label="Username" />
        <InputGroupAddon>@</InputGroupAddon>
        <TextInput placeholder="Server" aria-label="Server" />
      </InputGroup>

      <InputGroup>
        <InputGroupAddon>With textarea</InputGroupAddon>
        <Textarea aria-label="With textarea"></Textarea>
      </InputGroup>
    </>
  )
}

Wrapping

An input group wraps by default via flex-wrap: wrap, accommodating custom form field validation within the group. Disable this with the .flex-nowrap class.

@
JSX
import { InputGroup, InputGroupAddon, TextInput } from '@chassis-ui/react'

export const Example = () => {
  return (
    <InputGroup className="flex-nowrap">
      <InputGroupAddon id="addon-wrapping">@</InputGroupAddon>
      <TextInput placeholder="Username" aria-label="Username" aria-describedby="addon-wrapping" />
    </InputGroup>
  )
}

Sizing

Add the relative form sizing classes to <InputGroup> itself, and its contents resize automatically — no need to repeat the form control size classes on each element.

Sizing on the individual input group elements isn't supported.

Small
Default
Large
JSX
import { InputGroup, InputGroupAddon, TextInput } from '@chassis-ui/react'

export const Example = () => {
  return (
    <>
      <InputGroup size="sm" className="mb-md">
        <InputGroupAddon id="inputGroup-sizing-sm">Small</InputGroupAddon>
        <TextInput aria-label="Sizing example input" aria-describedby="inputGroup-sizing-sm" />
      </InputGroup>

      <InputGroup className="mb-md">
        <InputGroupAddon id="inputGroup-sizing-default">Default</InputGroupAddon>
        <TextInput aria-label="Sizing example input" aria-describedby="inputGroup-sizing-default" />
      </InputGroup>

      <InputGroup size="lg">
        <InputGroupAddon id="inputGroup-sizing-lg">Large</InputGroupAddon>
        <TextInput aria-label="Sizing example input" aria-describedby="inputGroup-sizing-lg" />
      </InputGroup>
    </>
  )
}

Checkboxes and radios

Place a checkbox or radio option within an input group's addon instead of text.

JSX
import {
  Checkbox,
  TextInput,
  Radio,
  RadioGroup,
  InputGroup,
  InputGroupAddon
} from '@chassis-ui/react'

export const Example = () => {
  return (
    <>
      <InputGroup className="mb-md">
        <InputGroupAddon>
          <Checkbox value="" aria-label="Checkbox for following text input" />
        </InputGroupAddon>
        <TextInput aria-label="Text input with checkbox" />
      </InputGroup>

      <InputGroup>
        <InputGroupAddon component="div">
          <RadioGroup aria-label="Radio button for following text input" defaultValue="">
            <Radio value="" aria-label="Radio button for following text input" />
          </RadioGroup>
        </InputGroupAddon>
        <TextInput aria-label="Text input with radio button" />
      </InputGroup>
    </>
  )
}

Multiple inputs

Multiple <TextInput>s are supported visually, though validation styles are only available for input groups with a single <TextInput>.

First and last name
JSX
import { InputGroup, InputGroupAddon, TextInput } from '@chassis-ui/react'

export const Example = () => {
  return (
    <InputGroup>
      <InputGroupAddon>First and last name</InputGroupAddon>
      <TextInput aria-label="First name" />
      <TextInput aria-label="Last name" />
    </InputGroup>
  )
}

Multiple addons

Multiple add-ons are supported and can be mixed with checkbox and radio input versions.

$0.00
$0.00
JSX
import { InputGroup, InputGroupAddon, TextInput } from '@chassis-ui/react'

export const Example = () => {
  return (
    <>
      <InputGroup className="mb-md">
        <InputGroupAddon>$</InputGroupAddon>
        <InputGroupAddon>0.00</InputGroupAddon>
        <TextInput aria-label="Dollar amount (with dot and two decimal places)" />
      </InputGroup>

      <InputGroup>
        <TextInput aria-label="Dollar amount (with dot and two decimal places)" />
        <InputGroupAddon>$</InputGroupAddon>
        <InputGroupAddon>0.00</InputGroupAddon>
      </InputGroup>
    </>
  )
}

Button addons

Buttons work as addons in place of text, on either side of the input or on both.

JSX
import { Button, InputGroup, TextInput } from '@chassis-ui/react'

export const Example = () => {
  return (
    <>
      <InputGroup className="mb-md">
        <Button type="button" color="secondary" variant="outline" id="button-addon1">
          Button
        </Button>
        <TextInput
          placeholder=""
          aria-label="Example text with button addon"
          aria-describedby="button-addon1"
        />
      </InputGroup>

      <InputGroup className="mb-md">
        <TextInput
          placeholder="Recipient's username"
          aria-label="Recipient's username"
          aria-describedby="button-addon2"
        />
        <Button type="button" color="secondary" variant="outline" id="button-addon2">
          Button
        </Button>
      </InputGroup>

      <InputGroup className="mb-md">
        <Button type="button" color="secondary" variant="outline">
          Button
        </Button>
        <Button type="button" color="secondary" variant="outline">
          Button
        </Button>
        <TextInput placeholder="" aria-label="Example text with two button addons" />
      </InputGroup>

      <InputGroup>
        <TextInput
          placeholder="Recipient's username"
          aria-label="Recipient's username with two button addons"
        />
        <Button type="button" color="secondary" variant="outline">
          Button
        </Button>
        <Button type="button" color="secondary" variant="outline">
          Button
        </Button>
      </InputGroup>
    </>
  )
}

Buttons with menus

A <Menu> composes into an input group the same way a plain button addon does.

JSX
import {
  TextInput,
  InputGroup,
  Menu,
  MenuToggle,
  MenuList,
  MenuItem,
  MenuDivider
} from '@chassis-ui/react'

export const Example = () => {
  return (
    <>
      <InputGroup className="mb-md">
        <Menu>
          <MenuToggle color="secondary" variant="outline">
            Menu
          </MenuToggle>
          <MenuList>
            <MenuItem href="#">Action</MenuItem>
            <MenuItem href="#">Another action</MenuItem>
            <MenuItem href="#">Something else here</MenuItem>
            <MenuDivider />
            <MenuItem href="#">Separated link</MenuItem>
          </MenuList>
        </Menu>
        <TextInput aria-label="Text input with menu button" />
      </InputGroup>

      <InputGroup className="mb-md">
        <TextInput aria-label="Text input with menu button" />
        <Menu placement="bottom-end">
          <MenuToggle color="secondary" variant="outline">
            Menu
          </MenuToggle>
          <MenuList>
            <MenuItem href="#">Action</MenuItem>
            <MenuItem href="#">Another action</MenuItem>
            <MenuItem href="#">Something else here</MenuItem>
            <MenuDivider />
            <MenuItem href="#">Separated link</MenuItem>
          </MenuList>
        </Menu>
      </InputGroup>

      <InputGroup>
        <Menu>
          <MenuToggle color="secondary" variant="outline">
            Menu
          </MenuToggle>
          <MenuList>
            <MenuItem href="#">Action</MenuItem>
            <MenuItem href="#">Another action</MenuItem>
            <MenuItem href="#">Something else here</MenuItem>
            <MenuDivider />
            <MenuItem href="#">Separated link</MenuItem>
          </MenuList>
        </Menu>
        <TextInput aria-label="Text input with 2 menu buttons" />
        <Menu placement="bottom-end">
          <MenuToggle color="secondary" variant="outline">
            Menu
          </MenuToggle>
          <MenuList>
            <MenuItem href="#">Action</MenuItem>
            <MenuItem href="#">Another action</MenuItem>
            <MenuItem href="#">Something else here</MenuItem>
            <MenuDivider />
            <MenuItem href="#">Separated link</MenuItem>
          </MenuList>
        </Menu>
      </InputGroup>
    </>
  )
}

Segmented buttons

Pair a button addon with an icon-only <Menu> toggle to split a primary action from a menu of related ones.

JSX
import {
  Button,
  TextInput,
  InputGroup,
  Menu,
  MenuToggle,
  MenuList,
  MenuItem,
  MenuDivider
} from '@chassis-ui/react'

export const Example = () => {
  return (
    <>
      <InputGroup className="mb-md">
        <Button type="button" color="secondary" variant="outline">
          Action
        </Button>
        <Menu>
          <MenuToggle color="secondary" variant="outline">
            <span className="visually-hidden">Toggle menu</span>
          </MenuToggle>
          <MenuList>
            <MenuItem href="#">Action</MenuItem>
            <MenuItem href="#">Another action</MenuItem>
            <MenuItem href="#">Something else here</MenuItem>
            <MenuDivider />
            <MenuItem href="#">Separated link</MenuItem>
          </MenuList>
        </Menu>
        <TextInput aria-label="Text input with segmented menu button" />
      </InputGroup>

      <InputGroup>
        <TextInput aria-label="Text input with segmented menu button" />
        <Button type="button" color="secondary" variant="outline">
          Action
        </Button>
        <Menu placement="bottom-end">
          <MenuToggle color="secondary" variant="outline">
            <span className="visually-hidden">Toggle menu</span>
          </MenuToggle>
          <MenuList>
            <MenuItem href="#">Action</MenuItem>
            <MenuItem href="#">Another action</MenuItem>
            <MenuItem href="#">Something else here</MenuItem>
            <MenuDivider />
            <MenuItem href="#">Separated link</MenuItem>
          </MenuList>
        </Menu>
      </InputGroup>
    </>
  )
}

Custom forms

Input groups support custom selects and custom file inputs. Browser default versions of these controls aren't supported.

Custom select

A <Select> composes into an input group the same way a <TextInput> does, with an addon on either side.

JSX
import { Button, InputGroup, InputGroupAddon, Select } from '@chassis-ui/react'

export const Example = () => {
  return (
    <>
      <InputGroup className="mb-md">
        <InputGroupAddon component="label" htmlFor="inputGroupSelect01">
          Options
        </InputGroupAddon>
        <Select id="inputGroupSelect01">
          <option>Choose...</option>
          <option value="1">One</option>
          <option value="2">Two</option>
          <option value="3">Three</option>
        </Select>
      </InputGroup>

      <InputGroup className="mb-md">
        <Select id="inputGroupSelect02">
          <option>Choose...</option>
          <option value="1">One</option>
          <option value="2">Two</option>
          <option value="3">Three</option>
        </Select>
        <InputGroupAddon component="label" htmlFor="inputGroupSelect02">
          Options
        </InputGroupAddon>
      </InputGroup>

      <InputGroup className="mb-md">
        <Button type="button" color="secondary" variant="outline">
          Button
        </Button>
        <Select id="inputGroupSelect03" aria-label="Example select with button addon">
          <option>Choose...</option>
          <option value="1">One</option>
          <option value="2">Two</option>
          <option value="3">Three</option>
        </Select>
      </InputGroup>

      <InputGroup>
        <Select id="inputGroupSelect04" aria-label="Example select with button addon">
          <option>Choose...</option>
          <option value="1">One</option>
          <option value="2">Two</option>
          <option value="3">Three</option>
        </Select>
        <Button type="button" color="secondary" variant="outline">
          Button
        </Button>
      </InputGroup>
    </>
  )
}

Custom file input

A <FileInput> composes into an input group the same way, pairing with a label-style addon or a button.

JSX
import { Button, FileInput, InputGroup, InputGroupAddon } from '@chassis-ui/react'

export const Example = () => {
  return (
    <>
      <InputGroup className="mb-md">
        <InputGroupAddon component="label" htmlFor="inputGroupFile01">
          Upload
        </InputGroupAddon>
        <FileInput id="inputGroupFile01" />
      </InputGroup>

      <InputGroup className="mb-md">
        <FileInput id="inputGroupFile02" />
        <InputGroupAddon component="label" htmlFor="inputGroupFile02">
          Upload
        </InputGroupAddon>
      </InputGroup>

      <InputGroup className="mb-md">
        <Button type="button" color="secondary" variant="outline" id="inputGroupFileAddon03">
          Button
        </Button>
        <FileInput
          id="inputGroupFile03"
          aria-describedby="inputGroupFileAddon03"
          aria-label="Upload"
        />
      </InputGroup>

      <InputGroup>
        <FileInput
          id="inputGroupFile04"
          aria-describedby="inputGroupFileAddon04"
          aria-label="Upload"
        />
        <Button type="button" color="secondary" variant="outline" id="inputGroupFileAddon04">
          Button
        </Button>
      </InputGroup>
    </>
  )
}

API

InputGroup

PropTypeDefault
asChild?boolean—
Render the single child element in place of this component's own element, with this component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log in</Link></Button>. The alternative to component for React Server Components — an element can be passed from the server to this package's client components, a component reference (component={Link}) can't.
className?string—
A string of all className you want applied to the component.
component?ElementType—
Component used for the root node. Either a string to use a HTML element or a component.
ref?any—
size?"sm" | "lg"—
Size the component sm or lg.

InputGroupAddon

PropTypeDefault
asChild?boolean—
Render the single child element in place of this component's own element, with this component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log in</Link></Button>. The alternative to component for React Server Components — an element can be passed from the server to this package's client components, a component reference (component={Link}) can't.
className?string—
A string of all className you want applied to the component.
component?ElementType—
Component used for the root node. Either a string to use a HTML element or a component.
htmlFor?string—
The id of the form control this addon labels, when rendered as component="label".
ref?any—