InputGroup
freeInputGroup 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 InputGroupExamples
Addons
Preview
Dark
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
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.
| Prop | Description | Type | Default |
|---|---|---|---|
children | Inputs, buttons, selects, addons, or compatible controls rendered as one joined row. | ReactNode | - |
className | Class names applied to the group root. | string | - |
ref | Ref forwarded to the group root element. | Ref<HTMLDivElement> | - |
size | Control size shared by group-aware descendants. Falls back to Form size, then ConfigProvider control size. | ControlSize | ConfigProvider controlSize |
InputGroup.Addon
Addon reads the props below and renders a non-editable segment inside an InputGroup.
| Prop | Description | Type | Default |
|---|---|---|---|
children | Addon content, such as a unit, symbol, domain suffix, or short label. | ReactNode | - |
className | Class names applied to the addon root. | string | - |
ref | Ref forwarded to the addon root element. | Ref<HTMLDivElement> | - |
size | Control size for the addon. Falls back to InputGroup size, then Form size, then ConfigProvider control size. | ControlSize | ConfigProvider controlSize |
Types
type ControlSize = 'sm' | 'md' | 'lg';