Input Group
An input with icons, text and buttons attached on either side.
⌘K
import { Kbd } from '@/components/ui/kbd'
import { InputGroup, InputGroupAddon, InputGroupInput } from '@/components/ui/input-group'
export default function InputGroupDemo() {
return (
<InputGroup className="max-w-sm">
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
<InputGroupInput type="search" placeholder="Search components…" aria-label="Search" />
<InputGroupAddon align="inline-end">
<Kbd>⌘K</Kbd>
</InputGroupAddon>
</InputGroup>
)
}
function SearchIcon() {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
<circle cx="11" cy="11" r="8" />
<path d="m21 21-4.3-4.3" />
</svg>
)
}
Installation
pnpm dlx shadcn@latest add https://kagadmodyaa.vercel.app/r/input-group.jsonNeeds a React + Tailwind project set up with shadcn. Copies the component source straight into your project, so you can edit it.
Input Group imports cn() from lib/utils.ts — add it too if you don't have it yet:
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Usage
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText, InputGroupButton } from '@/components/ui/input-group'
<InputGroup>
<InputGroupAddon>
<InputGroupText>https://</InputGroupText>
</InputGroupAddon>
<InputGroupInput placeholder="example.com" />
<InputGroupAddon align="inline-end">
<InputGroupButton>Go</InputGroupButton>
</InputGroupAddon>
</InputGroup>Examples
Text addons and a copy button
https://
.kagadmodyaa.com
'use client'
import * as React from 'react'
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText } from '@/components/ui/input-group'
/** Text addons on both sides, and a button that copies the value. */
export default function InputGroupAddons() {
const [slug, setSlug] = React.useState('my-studio')
const [copied, setCopied] = React.useState(false)
const url = `https://${slug}.kagadmodyaa.com`
async function copy() {
try {
await navigator.clipboard.writeText(url)
} catch {
// Clipboard blocked: still give feedback.
}
setCopied(true)
setTimeout(() => setCopied(false), 1500)
}
return (
<div className="grid w-full max-w-sm gap-4">
<InputGroup>
<InputGroupAddon>
<InputGroupText>https://</InputGroupText>
</InputGroupAddon>
<InputGroupInput value={slug} onChange={event => setSlug(event.target.value)} aria-label="Subdomain" />
<InputGroupAddon align="inline-end">
<InputGroupText>.kagadmodyaa.com</InputGroupText>
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput readOnly value={url} aria-label="Share link" />
<InputGroupAddon align="inline-end">
<InputGroupButton aria-label="Copy link" onClick={copy}>
{copied ? <CheckIcon /> : <CopyIcon />}
{copied ? 'Copied' : 'Copy'}
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
</div>
)
}
function CopyIcon() {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
<rect width="14" height="14" x="8" y="8" rx="2" />
<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
</svg>
)
}
function CheckIcon() {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
<path d="M20 6 9 17l-5-5" />
</svg>
)
}
API Reference
| Name | Type | Default | Description |
|---|---|---|---|
| InputGroup | div props | — | The bordered wrapper. It shows the focus ring when the input inside is focused, and the invalid look when the input has aria-invalid. |
| InputGroupAddon align | 'inline-start' | 'inline-end' | 'inline-start' | Which side of the input the addon sits on. Clicking an addon focuses the input. |
| InputGroupInput | input props | — | The borderless input. Use it instead of Input inside a group. |
| InputGroupText | span props | — | Plain text in an addon — a URL prefix, a unit, a currency sign. |
| InputGroupButton | Button props | variant 'ghost', size 'sm' | A compact button that fits inside the group. |