Browse the docs
ComponentsNo. 31 of 109
Input Group
An input with icons, text or buttons attached on either side.
Installation
pnpm dlx shadcn@latest add @corsair-ui/input-groupNo setup needed: the CLI finds @corsair-ui in the shadcn registry directory. The files land in your project as source: edit them like your own code. More in Installation.
Examples
With an icon
Text addons
https://
.corsair
With a button
Invalid
https://
Accessibility
- The group draws the focus ring for the input inside.
- Buttons inside it need their own aria-label when they only show an icon.
Compatibility
- Tailwind CSS
- 3.4 and 4, both checked in CI
- React
- 19
- Rendering
- Works in server components
Source
1 file, installed as source you own
input-group.tsxView on GitHubimport { cva, type VariantProps } from "class-variance-authority";
import type { ComponentProps } from "react";
import { cn } from "@/registry/default/lib/utils";
import { Input } from "@/registry/default/ui/input";
/**
* An input with icons, text or buttons attached. The group draws the border
* and the focus ring; the input inside is borderless.
*
* @example
* <InputGroup>
* <InputGroupAddon><SearchIcon /></InputGroupAddon>
* <InputGroupInput placeholder="Search" aria-label="Search" />
* </InputGroup>
*/
function InputGroup({ className, ...props }: ComponentProps<"div">) {
return (
<div
role="group"
data-slot="input-group"
className={cn(
"border-input bg-field relative flex h-9 w-full min-w-0 items-center rounded-md border",
"transition-[color,border-color,box-shadow] outline-none motion-reduce:transition-none",
"has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]",
"has-[[aria-invalid=true]]:border-destructive has-[[aria-invalid=true]]:ring-destructive/20",
"has-[[data-slot=input-group-control]:disabled]:opacity-50",
className
)}
{...props}
/>
);
}
const inputGroupAddonVariants = cva(
"text-muted-foreground flex h-full shrink-0 items-center gap-2 text-sm select-none [&>svg:not([class*='size-'])]:size-4",
{
variants: {
align: {
"inline-start": "order-first pl-3",
"inline-end": "order-last pr-3",
},
},
defaultVariants: {
align: "inline-start",
},
}
);
/** Icons, text or a small button on either side of the input. Order in the markup does not matter. */
function InputGroupAddon({
className,
align = "inline-start",
...props
}: ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {
return (
<div
data-slot="input-group-addon"
data-align={align}
className={cn(inputGroupAddonVariants({ align }), className)}
{...props}
/>
);
}
function InputGroupText({ className, ...props }: ComponentProps<"span">) {
return (
<span
data-slot="input-group-text"
className={cn("text-muted-foreground flex items-center gap-2 text-sm", className)}
{...props}
/>
);
}
function InputGroupInput({ className, ...props }: ComponentProps<"input">) {
return (
<Input
data-slot="input-group-control"
className={cn(
"h-full flex-1 rounded-none border-0 bg-transparent",
"focus-visible:ring-0 disabled:opacity-100 aria-[invalid=true]:ring-0",
className
)}
{...props}
/>
);
}
export { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText };