Components
Input Group
An Input with attached context: a leading or trailing icon, a prefix or suffix, or a single action, all inside one visual boundary so the field still reads as a single control.
Input Group is a composed pattern built on the Input primitive. It does not replace Input. It extends it for the cases where attached context is required.
Preview
Rendered with live Cognition tokens. Focus it and the whole group rings, no dark: classes.
Variants
<InputGroup leadingIcon={<Search />} /><InputGroup trailingIcon={<Check />} /><InputGroup leadingText="https://" /><InputGroup trailingText="USD" /><InputGroup trailingAction={<Button size="sm">…</Button>} /><InputGroup leadingIcon={<AtSign />} trailingAction={…} />Leading and trailing slots compose: an icon, a prefix or suffix, an action, or an icon paired with an action.
States
Default. Empty and at rest.
Focused. The whole group rings (shown statically here).
Filled. Holds a value.
<InputGroup error /><InputGroup disabled />Focus rings the entire boundary, not just the inner field. Error swaps the border and ring to the danger token.
API
All standard input props pass through to the inner field.
Don't and Do
Don't stack multiple trailing actions in one group. Two or more competing buttons crowd the field and blur which one the value belongs to. When more than one action is needed, move them out of the field into a different pattern.
<InputGroup
leadingIcon={<Search />}
placeholder="Search the workspace..."
trailingAction={<Button size="sm">Search</Button>}
/>Use it to add contextual affordance to an input without breaking the field's visual boundary.
import { InputGroup } from "@/components/ui/input-group";
import { Globe } from "lucide-react";
export function SiteField() {
return (
<InputGroup
leadingIcon={<Globe />}
leadingText="https://"
trailingText=".distyl.ai"
placeholder="workspace"
/>
);
}