InputGroup

free

InputGroup joins related fields and controls so they read as one input row.

Preview
Dark
import { PiMagnifyingGlass } from 'react-icons/pi'
import Button from '@/components/ui/Button';
import Input from '@/components/ui/Input';
import InputGroup from '@/components/ui/InputGroup';

export default function UsageDemo() {
  return (
    <InputGroup className="mx-auto w-full max-w-md">
      <Input
        aria-label="Search customers"
        placeholder="Search customers"
        prefix={<PiMagnifyingGlass />}
      />
      <Button>Search</Button>
    </InputGroup>
  );
}

Installation

Add this component with the NateUI CLI.

npx nateui@latest add InputGroup

Examples

Addons

Preview
Dark
@
.nateui.dev
https://
.com
to
import Input from '@/components/ui/Input';
import InputGroup from '@/components/ui/InputGroup';

export default function AddonsDemo() {
  return (
    <div className="mx-auto w-full max-w-md space-y-4">
      <InputGroup>
        <InputGroup.Addon>@</InputGroup.Addon>
        <Input placeholder="username" />
      </InputGroup>

      <InputGroup>
        <Input placeholder="workspace" />
        <InputGroup.Addon>.nateui.dev</InputGroup.Addon>
      </InputGroup>

      <InputGroup>
        <InputGroup.Addon>https://</InputGroup.Addon>
        <Input placeholder="company" />
        <InputGroup.Addon>.com</InputGroup.Addon>
      </InputGroup>

      <InputGroup>
        <Input placeholder="Min" />
        <InputGroup.Addon>to</InputGroup.Addon>
        <Input placeholder="Max" />
      </InputGroup>
    </div>
  );
}

With Buttons

Preview
Dark
import {
    PiMagnifyingGlass,
    PiMicrophone,
    PiPaperPlaneTilt,
} from 'react-icons/pi'
import Button from '@/components/ui/Button';
import Input from '@/components/ui/Input';
import InputGroup from '@/components/ui/InputGroup';

export default function WithButtonsDemo() {
  return (
    <div className="mx-auto w-full max-w-md space-y-4">
      <InputGroup>
        <Input placeholder="Search orders" />
        <Button>Search</Button>
      </InputGroup>
    </div>
  );
}

Button Group

Preview
Dark
import { useState } from 'react';
import Button from '@/components/ui/Button';
import InputGroup from '@/components/ui/InputGroup';

type ViewMode = 'day' | 'week' | 'month';

const viewModes: { label: string; value: ViewMode }[] = [
  { label: 'Day', value: 'day' },
  { label: 'Week', value: 'week' },
  { label: 'Month', value: 'month' },
];

export default function ButtonGroupDemo() {
  const [viewMode, setViewMode] = useState<ViewMode>('week');

  return (
    <InputGroup className="mx-auto">
      {viewModes.map((item) => (
        <Button
          key={item.value}
          active={viewMode === item.value}
          onClick={() => setViewMode(item.value)}
        >
          {item.label}
        </Button>
      ))}
    </InputGroup>
  );
}

Size

Preview
Dark
@
@
@
import Button from '@/components/ui/Button';
import Input from '@/components/ui/Input';
import InputGroup from '@/components/ui/InputGroup';

export default function SizeDemo() {
  return (
    <div className="mx-auto w-full max-w-md space-y-4">
      <InputGroup size="sm">
        <InputGroup.Addon>@</InputGroup.Addon>
        <Input placeholder="Small" />
      </InputGroup>

      <InputGroup>
        <InputGroup.Addon>@</InputGroup.Addon>
        <Input placeholder="Medium" />
      </InputGroup>

      <InputGroup size="lg">
        <InputGroup.Addon>@</InputGroup.Addon>
        <Input placeholder="Large" />
      </InputGroup>
    </div>
  );
}

Mixed Controls

Preview
Dark
    Plan
      $
        import Button from '@/components/ui/Button';
        import Input from '@/components/ui/Input';
        import InputGroup from '@/components/ui/InputGroup';
        import Select from '@/components/ui/Select';
        
        const domains = [
          { label: '.com', value: '.com' },
          { label: '.io', value: '.io' },
          { label: '.dev', value: '.dev' },
        ];
        
        const plans = [
          { label: 'Free', value: 'free' },
          { label: 'Team', value: 'team' },
          { label: 'Enterprise', value: 'enterprise' },
        ];
        
        const currencies = [
          { label: 'USD', value: 'usd' },
          { label: 'MYR', value: 'myr' },
          { label: 'SGD', value: 'sgd' },
        ];
        
        export default function MixedControlsDemo() {
          return (
            <div className="mx-auto w-full max-w-lg space-y-4">
              <InputGroup>
                <Input placeholder="workspace" />
                <Select
                  className="min-w-28"
                  isSearchable={false}
                  defaultValue={domains[0]}
                  options={domains}
                />
              </InputGroup>
        
              <InputGroup>
                <InputGroup.Addon>Plan</InputGroup.Addon>
                <Select
                  className="flex-1"
                  isSearchable={false}
                  defaultValue={plans[1]}
                  options={plans}
                />
                <Button>Apply</Button>
              </InputGroup>
        
              <InputGroup>
                <InputGroup.Addon>$</InputGroup.Addon>
                <Input defaultValue="79" aria-label="Price" />
                <Select
                  className="min-w-28"
                  isSearchable={false}
                  defaultValue={currencies[0]}
                  options={currencies}
                />
              </InputGroup>
            </div>
          );
        }

        API

        InputGroup is a compound component: InputGroup provides the joined row and size context, while InputGroup.Addon renders non-editable affix content.

        InputGroup

        InputGroup reads the props below and provides size to descendant Input, Select, Button, and Addon controls.

        PropDescriptionTypeDefault
        childrenInputs, buttons, selects, addons, or compatible controls rendered as one joined row.ReactNode-
        classNameClass names applied to the group root.string-
        refRef forwarded to the group root element.Ref<HTMLDivElement>-
        sizeControl size shared by group-aware descendants. Falls back to Form size, then ConfigProvider control size.ControlSizeConfigProvider controlSize

        InputGroup.Addon

        Addon reads the props below and renders a non-editable segment inside an InputGroup.

        PropDescriptionTypeDefault
        childrenAddon content, such as a unit, symbol, domain suffix, or short label.ReactNode-
        classNameClass names applied to the addon root.string-
        refRef forwarded to the addon root element.Ref<HTMLDivElement>-
        sizeControl size for the addon. Falls back to InputGroup size, then Form size, then ConfigProvider control size.ControlSizeConfigProvider controlSize

        Types

        type ControlSize = 'sm' | 'md' | 'lg';