Import
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
InputGroupText,
InputGroupTextarea,
} from "@timelycare/helix-ui"
Props
interface InputGroupProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string
children: React.ReactNode
}
interface InputGroupInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
className?: string
}
interface InputGroupTextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
className?: string
}
interface InputGroupAddonProps extends React.HTMLAttributes<HTMLDivElement> {
align?: "inline-start" | "inline-end" | "block-start" | "block-end"
className?: string
children: React.ReactNode
}
interface InputGroupButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: "default" | "secondary" | "ghost" | "outline"
size?: "default" | "sm" | "xs" | "icon-xs" | "icon-sm"
className?: string
children: React.ReactNode
}
interface InputGroupTextProps extends React.HTMLAttributes<HTMLSpanElement> {
className?: string
children: React.ReactNode
}
Styling
Container
| Property | Value | Tailwind |
|---|---|---|
| Display | Flex | flex |
| Border | 1px solid | border border-input |
| Border radius | 8px | rounded-md |
| Background | bg-input-bg | Theme-specific input background |
| Min width | min-w-0 | Prevents flex overflow |
| Focus-within | Ring | focus-within:ring-[3px] focus-within:ring-ring/50 focus-within:border-ring |
| Shadow | Subtle | shadow-xs |
Input
| Property | Value | Tailwind |
|---|---|---|
| Height | 36px | h-9 |
| Border | None (container provides border) | border-0 |
| Focus | No ring (container handles focus) | focus-visible:ring-0 |
| Shadow | None | shadow-none |
Addon
| Property | Value | Tailwind |
|---|---|---|
| Display | Flex, centered | flex items-center |
| Padding | 0 12px | px-3 |
| Color | Muted | text-muted-foreground |
| Icon size | 16px | [&_svg]:size-4 |
Typography
- Text addons: Adelle Sans Regular, 14px (
text-sm),text-muted-foreground - Input text: Adelle Sans Regular, 14px (
text-sm),text-foreground
Addon Alignment
| Align | Position | Use For |
|---|---|---|
inline-start (default) | Left side of input | Prefix icons, currency symbols |
inline-end | Right side of input | Suffix icons, validation indicators |
block-start | Above the input | Toolbar header (code editor) |
block-end | Below the input | Status bar, character counts |
Common Patterns
Icon Prefix
<InputGroup>
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
<InputGroupInput placeholder="Search..." />
</InputGroup>
Icon Suffix
<InputGroup>
<InputGroupInput type="email" placeholder="Enter your email" />
<InputGroupAddon align="inline-end">
<MailIcon />
</InputGroupAddon>
</InputGroup>
Both Sides
<InputGroup>
<InputGroupAddon>
<CreditCardIcon />
</InputGroupAddon>
<InputGroupInput placeholder="Card number" />
<InputGroupAddon align="inline-end">
<CheckIcon />
</InputGroupAddon>
</InputGroup>
Text Addons (Currency)
<InputGroup>
<InputGroupAddon>
<InputGroupText>$</InputGroupText>
</InputGroupAddon>
<InputGroupInput placeholder="0.00" />
<InputGroupAddon align="inline-end">
<InputGroupText>USD</InputGroupText>
</InputGroupAddon>
</InputGroup>
URL Input
<InputGroup>
<InputGroupAddon>
<InputGroupText>https://</InputGroupText>
</InputGroupAddon>
<InputGroupInput placeholder="example.com" />
</InputGroup>
With Button
<InputGroup>
<InputGroupInput placeholder="Type to search..." />
<InputGroupAddon align="inline-end">
<InputGroupButton variant="secondary">Search</InputGroupButton>
</InputGroupAddon>
</InputGroup>
With Copy Button
<InputGroup>
<InputGroupInput placeholder="https://example.com" readOnly />
<InputGroupAddon align="inline-end">
<InputGroupButton aria-label="Copy" size="icon-xs">
<CopyIcon />
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
With Spinner (Loading)
<InputGroup data-disabled>
<InputGroupInput placeholder="Searching..." disabled />
<InputGroupAddon align="inline-end">
<Spinner />
</InputGroupAddon>
</InputGroup>
Textarea with Footer
<InputGroup>
<InputGroupTextarea placeholder="Enter your message" />
<InputGroupAddon align="block-end">
<InputGroupText className="text-muted-foreground text-xs">
120 characters left
</InputGroupText>
</InputGroupAddon>
</InputGroup>
Code Editor Pattern
<InputGroup>
<InputGroupTextarea placeholder="console.log('hello');" className="min-h-[200px]" />
<InputGroupAddon align="block-start" className="border-b">
<InputGroupText className="font-mono font-medium">script.js</InputGroupText>
<InputGroupButton className="ml-auto" size="icon-xs">
<CopyIcon />
</InputGroupButton>
</InputGroupAddon>
<InputGroupAddon align="block-end" className="border-t">
<InputGroupText>Line 1, Column 1</InputGroupText>
<InputGroupButton size="sm" className="ml-auto" variant="default">
Run
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
Accessibility
InputGroupInputis a standard<input>element — inherits all native accessibilityInputGroupTextareais a standard<textarea>— inherits all native accessibility- Icon addons are decorative; use
aria-labelon buttons within addons - Link
aria-describedbyto any visible hint/description text data-disabledon the container provides visual disabled state; also setdisabledon the input
Gotchas
| Problem | Solution |
|---|---|
| Double focus ring | InputGroupInput strips its own ring; the container handles focus |
| Addon not vertically centered | Ensure items-center is on the addon (default behavior) |
| Button addon wrong size | Use size="icon-xs" or size="sm" for buttons inside addons |
| Textarea addon on wrong side | Use align="block-start" (above) or align="block-end" (below) |
| Disabled state doesn't dim addons | Add data-disabled to the InputGroup container |
| Custom radius needed | Override with className="[--radius:9999px]" on InputGroup |
See Also
- Related Components: Input (standalone input), Textarea (standalone textarea), Button Group (button container), Spinner (loading indicator in addons)
- Tokens: Colors —
input,ring,muted-foregroundtokens
Last updated: February 24, 2026