YA Input Group
A composable input group component for combining inputs with addons like icons, text, and buttons
The YaInputGroup is a flexible composition component in the YaVendio Design System. It allows you to combine inputs or textareas with addons such as icons, text, buttons, and more at various positions.
Quick Start
pnpm dlx shadcn@latest add @yavendio/ya-input-groupnpx shadcn@latest add @yavendio/ya-input-groupyarn dlx shadcn@latest add @yavendio/ya-input-groupimport {
YaInputGroup,
YaInputGroupAddon,
YaInputGroupInput,
} from '@/components/ui/ya-input-group'
import { Search } from 'lucide-react'
export default function Example() {
return (
<YaInputGroup>
<YaInputGroupInput placeholder="Search..." />
<YaInputGroupAddon>
<Search />
</YaInputGroupAddon>
</YaInputGroup>
)
}Basic Usage
The component uses a composition pattern with several subcomponents:
<YaInputGroup>
<YaInputGroupInput placeholder="Search..." />
<YaInputGroupAddon>
<Search />
</YaInputGroupAddon>
<YaInputGroupAddon align="inline-end">12 results</YaInputGroupAddon>
</YaInputGroup>Component Structure
| Component | Description |
|---|---|
YaInputGroup | Container wrapper with border and focus states |
YaInputGroupAddon | Addon container with position alignment |
YaInputGroupInput | Styled input element |
YaInputGroupTextarea | Styled textarea element |
YaInputGroupButton | Button for use within addons |
YaInputGroupText | Text element for use within addons |
Addon Alignment
The align prop on YaInputGroupAddon positions the addon relative to the input.
| Align | Description | Usage |
|---|---|---|
inline-start | Left side of input (default) | Icons, prefixes |
inline-end | Right side of input | Suffixes, buttons |
block-start | Above textarea (header bar) | File names, tabs |
block-end | Below textarea (footer bar) | Character count, submit |
For proper focus management, YaInputGroupAddon should always be placed after YaInputGroupInput or YaInputGroupTextarea in the DOM. Use the align prop to visually position the addon.
Examples
With Icons
<YaInputGroup>
<YaInputGroupInput placeholder="Search..." />
<YaInputGroupAddon>
<Search />
</YaInputGroupAddon>
</YaInputGroup>
<YaInputGroup>
<YaInputGroupInput placeholder="Card number" />
<YaInputGroupAddon>
<CreditCard />
</YaInputGroupAddon>
<YaInputGroupAddon align="inline-end">
<Check />
</YaInputGroupAddon>
</YaInputGroup>With Text
<YaInputGroup>
<YaInputGroupInput placeholder="0.00" />
<YaInputGroupAddon>
<YaInputGroupText>$</YaInputGroupText>
</YaInputGroupAddon>
<YaInputGroupAddon align="inline-end">
<YaInputGroupText>USD</YaInputGroupText>
</YaInputGroupAddon>
</YaInputGroup>
<YaInputGroup>
<YaInputGroupInput placeholder="example.com" />
<YaInputGroupAddon>
<YaInputGroupText>https://</YaInputGroupText>
</YaInputGroupAddon>
</YaInputGroup>With Button
<YaInputGroup>
<YaInputGroupInput placeholder="Enter URL..." />
<YaInputGroupAddon>
<YaInputGroupText>https://</YaInputGroupText>
</YaInputGroupAddon>
<YaInputGroupAddon align="inline-end">
<YaInputGroupButton variant="default">Search</YaInputGroupButton>
</YaInputGroupAddon>
</YaInputGroup>With Spinner
<YaInputGroup>
<YaInputGroupInput placeholder="Saving..." disabled />
<YaInputGroupAddon align="inline-end">
<Loader2 className="animate-spin" />
</YaInputGroupAddon>
</YaInputGroup>With Textarea
For textareas, use block-start and block-end alignment for header/footer bars.
<YaInputGroup className="flex-col items-stretch">
<YaInputGroupTextarea
placeholder="console.log('Hello, world!');"
className="min-h-[160px]"
hasBlockStart
hasBlockEnd
/>
<YaInputGroupAddon align="block-start">
<YaInputGroupText className="font-mono font-medium text-foreground">
script.js
</YaInputGroupText>
<div className="flex items-center gap-1">
<YaInputGroupButton size="icon-xs" aria-label="Refresh">
<RefreshCw className="size-3" />
</YaInputGroupButton>
<YaInputGroupButton size="icon-xs" aria-label="Copy">
<Copy className="size-3" />
</YaInputGroupButton>
</div>
</YaInputGroupAddon>
<YaInputGroupAddon align="block-end">
<YaInputGroupText className="text-xs">Line 1, Column 1</YaInputGroupText>
<YaInputGroupButton variant="default" size="sm">
Run <Send className="ml-1 size-3" />
</YaInputGroupButton>
</YaInputGroupAddon>
</YaInputGroup>When using block addons with YaInputGroupTextarea, set hasBlockStart and/or hasBlockEnd to true to add proper padding for the absolute-positioned addon bars.
States
Destructive
Please enter a valid email address
<YaInputGroup destructive>
<YaInputGroupInput placeholder="Invalid email" />
<YaInputGroupAddon><Mail /></YaInputGroupAddon>
</YaInputGroup>Disabled
<YaInputGroup>
<YaInputGroupInput placeholder="Disabled input" disabled />
<YaInputGroupAddon><Search /></YaInputGroupAddon>
</YaInputGroup>Prompt Example
A common use case is a prompt input with character counter and submit button.
const [message, setMessage] = useState('')
const maxLength = 280
<YaInputGroup className="flex-col items-stretch">
<YaInputGroupTextarea
placeholder="Ask, Search or Chat"
value={message}
onChange={(e) => setMessage(e.target.value.slice(0, maxLength))}
className="min-h-[100px]"
hasBlockEnd
/>
<YaInputGroupAddon align="block-end">
<YaInputGroupText className="text-xs">
{message.length}/{maxLength}
</YaInputGroupText>
<YaInputGroupButton variant="default" size="sm">
Post
</YaInputGroupButton>
</YaInputGroupAddon>
</YaInputGroup>Props
YaInputGroup
| Prop | Type | Default | Description |
|---|---|---|---|
destructive | boolean | false | Applies error border styling |
className | string | - | Additional classes |
YaInputGroupAddon
| Prop | Type | Default | Description |
|---|---|---|---|
align | 'inline-start' | 'inline-end' | 'block-start' | 'block-end' | 'inline-start' | Addon position |
className | string | - | Additional classes |
YaInputGroupInput
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes |
Extends all standard <input> HTML attributes.
YaInputGroupTextarea
| Prop | Type | Default | Description |
|---|---|---|---|
hasBlockStart | boolean | false | Adds top padding for block-start addon |
hasBlockEnd | boolean | false | Adds bottom padding for block-end addon |
className | string | - | Additional classes |
Extends all standard <textarea> HTML attributes.
YaInputGroupButton
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'xs' | 'icon-xs' | 'sm' | 'icon-sm' | 'xs' | Button size |
variant | 'default' | 'ghost' | 'ghost' | Button variant |
className | string | - | Additional classes |
Extends all standard <button> HTML attributes.
YaInputGroupText
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes |
Extends all standard <span> HTML attributes.
Accessibility
Best Practices
| Do | Don't |
|---|---|
Use align prop to position addons | Manually position with CSS |
| Place addons after input in DOM | Put addons before input |
Use hasBlockStart/hasBlockEnd with textarea | Forget padding for block addons |
Use YaInputGroupText for text content | Use plain text nodes |
Use YaInputGroupButton for actions | Use external button components |
TypeScript
import { type VariantProps } from 'class-variance-authority'
import {
yaInputGroupVariants,
yaInputGroupAddonVariants,
yaInputGroupButtonVariants,
yaInputGroupInputVariants,
} from '@/components/ui/ya-input-group'
type InputGroupDestructive = VariantProps<typeof yaInputGroupVariants>['destructive']
type AddonAlign = VariantProps<typeof yaInputGroupAddonVariants>['align']
type ButtonSize = VariantProps<typeof yaInputGroupButtonVariants>['size']