nori-ui

InputGroup

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.

3.6 kBgzipped

At a glance

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.

  • One border around the whole compound — addons are not separate boxes.
  • Addons accept any ReactNode, so strings (@, .com, USD) and icons both work.
  • Clicking an addon focuses the input.
  • The whole group's border lights up when the input has focus (focus-within on web).
  • InputGroupInput extends the full TextInput API (label, helperText, error, disabled, …) so you don't lose anything by reaching for the integrated layout.
  • disabled and error on the group cascade visually to the addons.

Prefix

Direction:

Suffix

Direction:

Prefix and suffix

Direction:

With label, helper text, and error

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>.

<InputGroup>
    <InputGroup.Addon>@</InputGroup.Addon>
    <InputGroupInput
        label="Username"
        helperText="3–20 characters, no spaces."
        defaultValue="alice"
    />
</InputGroup>

Icon addons

Addons accept any ReactNode, so you can drop in an icon component:

import { MailIcon } from 'lucide-react';
 
<InputGroup>
    <InputGroup.Addon><MailIcon size={16} /></InputGroup.Addon>
    <InputGroup.Input label="Email" placeholder="you@example.com" />
</InputGroup>

containerClassName

Adds Tailwind classes to the outer wrapper (label + field row + helper text), as opposed to className which applies to the bordered field row only. Use it when you need the entire labeled group to participate in a parent layout — e.g. setting a fixed width across a form.

<InputGroup containerClassName="w-80">
    <InputGroup.Addon>@</InputGroup.Addon>
    <InputGroup.Input label="Handle" />
</InputGroup>

Disabled and error states

Pass 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.

<InputGroup disabled>
    <InputGroup.Addon>@</InputGroup.Addon>
    <InputGroup.Input defaultValue="locked" />
</InputGroup>
 
<InputGroup>
    <InputGroup.Addon>@</InputGroup.Addon>
    <InputGroup.Input error="Invalid format" defaultValue="not an email" />
</InputGroup>

API

<InputGroup>

PropTypeDefaultDescription
classNamestring
containerClassNamestring
disabledbooleanfalseMark the entire group as disabled — cascades visually to addons + input.
errorbooleanfalseMark the entire group as errored — cascades visually to addons + input.
testIDstring

<InputGroup.Addon>

PropTypeDefaultDescription
classNamestring
testIDstring

<InputGroup.Input>

Inherits every prop from TextInput.

On this page

Preview theme