- Accordion
- Alert
- Alert Dialog
- Autocomplete
- Avatar
- Badge
- Breadcrumb
- Button
- Card
- Checkbox
- Checkbox Group
- Collapsible
- Combobox
- Dialog
- EmptyNew
- Field
- Fieldset
- Form
- Frame
- Group
- Input
- Input GroupNew
- KbdNew
- Label
- Menu
- Meter
- Number Field
- Pagination
- Popover
- Preview Card
- Progress
- Radio Group
- Scroll Area
- Select
- Separator
- Sheet
- SkeletonNew
- Slider
- SpinnerNew
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Toggle Group
- Toolbar
- Tooltip
Input Group
A flexible component for grouping inputs with addons, buttons, and other elements.
import { SearchIcon } from "lucide-react";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput aria-label="Search" placeholder="Search" type="search" />
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
</InputGroup>
);
}
Installation
pnpm dlx shadcn@latest add @coss/input-group
Usage
import { Input } from "@/components/ui/input"
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
InputGroupText,
} from "@/components/ui/input-group"<InputGroup>
<InputGroupInput type="email" placeholder="Email" />
<InputGroupAddon>
<MailIcon />
</InputGroupAddon>
</InputGroup>Examples
With End Icon
import { MailIcon } from "lucide-react";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput aria-label="Email" placeholder="Email" type="email" />
<InputGroupAddon align="inline-end">
<MailIcon />
</InputGroupAddon>
</InputGroup>
);
}
With Start Text
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
InputGroupText,
} from "@/components/ui/input-group";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput
aria-label="Set your URL"
className="*:[input]:ps-1!"
placeholder="coss"
type="search"
/>
<InputGroupAddon>
<InputGroupText>i.cal.com/</InputGroupText>
</InputGroupAddon>
</InputGroup>
);
}
With End Text
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
InputGroupText,
} from "@/components/ui/input-group";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput
aria-label="Choose a username"
placeholder="Choose a username"
type="text"
/>
<InputGroupAddon align="inline-end">
<InputGroupText>@coss.com</InputGroupText>
</InputGroupAddon>
</InputGroup>
);
}
With Start and End Text
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
InputGroupText,
} from "@/components/ui/input-group";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput
aria-label="Enter your domain"
className="*:[input]:ps-1!"
placeholder="coss"
type="text"
/>
<InputGroupAddon>
<InputGroupText>https://</InputGroupText>
</InputGroupAddon>
<InputGroupAddon align="inline-end">
<InputGroupText>.com</InputGroupText>
</InputGroupAddon>
</InputGroup>
);
}
With Number Field
import {
InputGroup,
InputGroupAddon,
InputGroupText,
} from "@/components/ui/input-group";
import {
NumberField,
NumberFieldInput,
} from "@/components/ui/number-field";
export default function Particle() {
return (
<InputGroup>
<NumberField aria-label="Enter the amount" defaultValue={10}>
<NumberFieldInput className="text-left" />
</NumberField>
<InputGroupAddon>
<InputGroupText>€</InputGroupText>
</InputGroupAddon>
<InputGroupAddon align="inline-end">
<InputGroupText>EUR</InputGroupText>
</InputGroupAddon>
</InputGroup>
);
}
With Tooltip
"use client";
import { InfoIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
import {
Popover,
PopoverPopup,
PopoverTrigger,
} from "@/components/ui/popover";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput
aria-label="Password"
placeholder="Password"
type="password"
/>
<InputGroupAddon align="inline-end">
<Popover>
<PopoverTrigger
openOnHover
render={
<Button
aria-label="Password requirements"
size="icon-xs"
variant="ghost"
/>
}
>
<InfoIcon />
</PopoverTrigger>
<PopoverPopup side="top" tooltipStyle>
<p>Min. 8 characters</p>
</PopoverPopup>
</Popover>
</InputGroupAddon>
</InputGroup>
);
}
With Icon Button
"use client";
import { CheckIcon, CopyIcon } from "lucide-react";
import { useRef } from "react";
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard";
import { Button } from "@/components/ui/button";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
import {
Tooltip,
TooltipPopup,
TooltipTrigger,
} from "@/components/ui/tooltip";
export default function Particle() {
const { copyToClipboard, isCopied } = useCopyToClipboard();
const inputRef = useRef<HTMLInputElement>(null);
return (
<InputGroup>
<InputGroupInput
aria-label="Url"
defaultValue="https://coss.com"
ref={inputRef}
type="text"
/>
<InputGroupAddon align="inline-end">
<Tooltip>
<TooltipTrigger
render={
<Button
aria-label="Copy"
onClick={() => {
if (inputRef.current) {
copyToClipboard(inputRef.current.value);
}
}}
size="icon-xs"
variant="ghost"
/>
}
>
{isCopied ? <CheckIcon /> : <CopyIcon />}
</TooltipTrigger>
<TooltipPopup>
<p>Copy to clipboard</p>
</TooltipPopup>
</Tooltip>
</InputGroupAddon>
</InputGroup>
);
}
With Button
import { Button } from "@/components/ui/button";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput placeholder="Type to search…" type="search" />
<InputGroupAddon align="inline-end">
<Button size="xs" variant="secondary">
Search
</Button>
</InputGroupAddon>
</InputGroup>
);
}
With Badge
import { Badge } from "@/components/ui/badge";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput placeholder="Type to search…" type="search" />
<InputGroupAddon align="inline-end">
<Badge variant="info">Badge</Badge>
</InputGroupAddon>
</InputGroup>
);
}
With Keyboard Shortcut
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
import { Kbd } from "@/components/ui/kbd";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput placeholder="Search…" type="search" />
<InputGroupAddon align="inline-end">
<Kbd>⌘K</Kbd>
</InputGroupAddon>
</InputGroup>
);
}
With Inner Label
"use client";
import { InfoIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
import { Label } from "@/components/ui/label";
import {
Popover,
PopoverPopup,
PopoverTrigger,
} from "@/components/ui/popover";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput id="email-1" placeholder="[email protected]" type="email" />
<InputGroupAddon align="block-start">
<Label className="text-foreground" htmlFor="email-1">
Email
</Label>
<Popover>
<PopoverTrigger
className="ml-auto"
openOnHover
render={<Button className="-m-1" size="icon-xs" variant="ghost" />}
>
<InfoIcon />
</PopoverTrigger>
<PopoverPopup side="top" tooltipStyle>
<p>We'll use this to send you notifications</p>
</PopoverPopup>
</Popover>
</InputGroupAddon>
</InputGroup>
);
}
Small Size
import { SearchIcon } from "lucide-react";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput
aria-label="Search"
placeholder="Search"
size="sm"
type="search"
/>
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
</InputGroup>
);
}
Large Size
import { SearchIcon } from "lucide-react";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput
aria-label="Search"
placeholder="Search"
size="lg"
type="search"
/>
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
</InputGroup>
);
}
Disabled
import { ArrowRightIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput
aria-label="Subscribe to our newsletter"
disabled
placeholder="Your best email"
type="email"
/>
<InputGroupAddon align="inline-end">
<Button aria-label="Subscribe" disabled size="icon-xs" variant="ghost">
<ArrowRightIcon />
</Button>
</InputGroupAddon>
</InputGroup>
);
}
Loading
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
import { Spinner } from "@/components/ui/spinner";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput disabled placeholder="Searching…" type="search" />
<InputGroupAddon align="inline-end">
<Spinner />
</InputGroupAddon>
</InputGroup>
);
}
With Textarea
"use client";
import { ArrowUpIcon, PlusIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
InputGroup,
InputGroupAddon,
InputGroupText,
InputGroupTextarea,
} from "@/components/ui/input-group";
import {
Menu,
MenuItem,
MenuPopup,
MenuTrigger,
} from "@/components/ui/menu";
import {
Tooltip,
TooltipPopup,
TooltipTrigger,
} from "@/components/ui/tooltip";
export default function Particle() {
return (
<InputGroup>
<InputGroupTextarea placeholder="Ask, Search or Chat…" />
<InputGroupAddon align="block-end">
<Menu>
<Tooltip>
<TooltipTrigger
render={
<MenuTrigger
render={
<Button
aria-label="Add files"
className="rounded-full"
size="icon-sm"
variant="ghost"
/>
}
>
<PlusIcon />
</MenuTrigger>
}
/>
<TooltipPopup>Add files and more</TooltipPopup>
</Tooltip>
<MenuPopup align="start">
<MenuItem>Add photos & files</MenuItem>
<MenuItem>Create image</MenuItem>
<MenuItem>Thinking</MenuItem>
<MenuItem>Deep research</MenuItem>
</MenuPopup>
</Menu>
<InputGroupText className="ml-auto">78% used</InputGroupText>
<Tooltip>
<TooltipTrigger
render={
<Button
aria-label="Send"
className="rounded-full"
size="icon-sm"
variant="default"
>
<ArrowUpIcon />
</Button>
}
/>
<TooltipPopup>Send</TooltipPopup>
</Tooltip>
</InputGroupAddon>
</InputGroup>
);
}
API Reference
InputGroup
The main component that wraps inputs and addons.
| Prop | Type | Description |
|---|---|---|
className | string | Additional CSS classes to apply to the component |
...props | React.ComponentProps<'div'> | All standard div attributes are supported |
InputGroupAddon
A container for addons like icons, text, buttons, and other elements. Can be positioned at the start or end (inline), or top or bottom (block) of the input.
| Prop | Type | Default | Description |
|---|---|---|---|
align | 'inline-start' | 'inline-end' | 'block-start' | 'block-end' | 'inline-start' | The position of the addon relative to the input. Use inline-start or inline-end for inputs, and block-start or block-end for textareas |
className | string | Additional CSS classes to apply to the component | |
...props | React.ComponentProps<'div'> | All standard div attributes are supported |
For proper focus navigation, the InputGroupAddon component should be placed
after the input in the DOM order.
InputGroupText
A text container component for displaying text content within an InputGroupAddon. Automatically styles text with muted foreground color and handles icon sizing.
| Prop | Type | Description |
|---|---|---|
className | string | Additional CSS classes to apply to the component |
...props | React.ComponentProps<'span'> | All standard span attributes are supported |
InputGroupInput
A specialized input component for use within InputGroup. It's essentially an unstyled Input component that inherits styling from the parent InputGroup.
| Prop | Type | Description |
|---|---|---|
className | string | Additional CSS classes to apply to the component |
...props | InputProps | All props from the Input component are supported, including type, placeholder, disabled, etc. |
InputGroupTextarea
A specialized textarea component for use within InputGroup. It's essentially an unstyled Textarea component that inherits styling from the parent InputGroup.
| Prop | Type | Description |
|---|---|---|
className | string | Additional CSS classes to apply to the component |
...props | TextareaProps | All props from the Textarea component are supported, including placeholder, disabled, rows, etc. |
Comparing with shadcn
If you're already familiar with shadcn/ui, this guide highlights the small differences and similarities so you can get started with coss ui quickly.
Quick Checklist
- No
InputGroupButtoncomponent - use the regularButtoncomponent directly insideInputGroupAddoninstead - To disable an input group, disable the
InputGroupInputorInputGroupTextareadirectly (and any Button inside it) - no need to add adata-disabledattribute onInputGroup.
On This Page