InputGroup
FormInput composition with prefix, suffix, icon, and metadata slots.
Responsive viewport — resize the window to explore. Direction and density apply only to this preview. Theme applies to the docs too.
"use client";
import {
InputGroup,
InputGroupInput,
InputGroupAddon,
} from "@/components/ui/input-group";
import { Label } from "@dashboardpack/core/components/ui/label";
import { Search, Link } from "lucide-react";
import { useState } from "react";
export default function InputGroupExample() {
const [query, setQuery] = useState("");
return (
<div className="w-full max-w-sm space-y-5">
<div className="space-y-2">
<Label htmlFor="group-search">Search projects</Label>
<InputGroup>
<InputGroupAddon>
<Search aria-hidden />
</InputGroupAddon>
<InputGroupInput
id="group-search"
placeholder="Project name…"
value={query}
onChange={(event) => setQuery(event.target.value)}
/>
<InputGroupAddon>{query.length} chars</InputGroupAddon>
</InputGroup>
</div>
<div className="space-y-2">
<Label htmlFor="group-domain">Workspace URL</Label>
<InputGroup>
<InputGroupAddon aria-hidden>https://</InputGroupAddon>
<InputGroupInput id="group-domain" placeholder="your-team" />
<InputGroupAddon aria-hidden>.apex.app</InputGroupAddon>
</InputGroup>
</div>
<InputGroup>
<InputGroupAddon>
<Link aria-hidden />
</InputGroupAddon>
<InputGroupInput
disabled
aria-label="Managed domain"
value="Managed by your administrator"
/>
</InputGroup>
</div>
);
}Usage
Compose InputGroupAddon and InputGroupInput inside InputGroup. Add a separate label using the input id. Prefixes and suffixes are presentation; serialize their meaning explicitly when storing values.
import {
InputGroup,
InputGroupInput,
InputGroupAddon,
} from "@/components/ui/input-group";Shipped as editable source in src/components/ui/input-group.tsx in both apps. Copy the implementation below when moving it to another project. Copy the complete example from Source code for all imports and state. Add "use client" when using hooks or event handlers. See installation and theming for project setup.
Component source
The complete implementation is included in both apps. It uses the React, Radix, Lucide, and shared-core dependencies already installed in Apex. Keep your project’s semantic theme tokens when reusing it.
src/components/ui/input-group.tsx
import type { ComponentProps } from "react";
import { Input } from "@dashboardpack/core/components/ui/input";
import { cn } from "@dashboardpack/core/lib/utils";
export function InputGroup({ className, ...props }: ComponentProps<"div">) {
return (
<div
className={cn(
"flex min-h-9 items-center gap-2 rounded-md border border-input bg-background px-3 focus-within:ring-2 focus-within:ring-ring has-[:disabled]:opacity-50",
className,
)}
{...props}
/>
);
}
export function InputGroupAddon({
className,
...props
}: ComponentProps<"span">) {
return (
<span
className={cn(
"inline-flex shrink-0 items-center gap-1 text-sm text-muted-foreground [&_svg]:size-4",
className,
)}
{...props}
/>
);
}
export function InputGroupInput({
className,
...props
}: ComponentProps<typeof Input>) {
return (
<Input
className={cn(
"min-w-0 flex-1 border-0 bg-transparent px-0 shadow-none focus-visible:ring-0 dark:bg-transparent",
className,
)}
{...props}
/>
);
}API reference
Common props and defaults. Slot-specific props are named below; native elements also accept their standard HTML attributes. The component TypeScript definitions are the complete type reference.
| Prop | Type / Default | Description |
|---|---|---|
| children (InputGroup) | ReactNodeDefault: — | Compose addons, input, and optional actions. |
| ...inputProps (InputGroupInput) | native input attributesDefault: — | Value, onChange, type, id, disabled, and validation. |
| className | stringDefault: — | Extend a slot’s styles. |