ZaidanHomeDocsComponentsBlocksChartsTypesetCreate

Command Palette

Search for a command to run...

GitHub
New
Sections
  • Introduction
  • Components
  • Blocks
  • Installation
  • Customization
  • Dark Mode
  • Typeset
  • Zaidan Skills
  • FAQ
  • Roadmap
  • Changelog
Installation
  • Astro
  • Manual
  • Solid Start
  • TanStack Router
  • TanStack Start
  • Vite
Blocks
  • Data Grid
  • Event Calendar
  • Filters
  • Gantt
  • Image Crop
  • Kanban
  • Message Scroller
  • Questionnaire
  • Sortable
Components
  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Attachment
  • Avatar
  • Badge
  • Breadcrumb
  • Bubble
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Dialog
  • Drawer
  • Dropdown Menu
  • Empty
  • Field
  • Hover Card
  • Input
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Label
  • Marker
  • Menubar
  • Message
  • Native Select
  • Navigation Menu
  • Pagination
  • Popover
  • Progress
  • Radio Group
  • Resizable
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Skeleton
  • Slider
  • Spinner
  • Switch
  • Table
  • Tabs
  • Textarea
  • Toast
  • Toggle
  • Toggle Group
  • Tooltip

Input Group

Add addons, buttons, and helper content to inputs.

Input Group is a native Solid composition built from standard controls and Zaidan wrappers.

12 results
1
import { SearchIcon } from "lucide-solid";
2
import { InputGroup, InputGroupAddon, InputGroupInput } from "~/components/ui/input-group";
3
4
export default function InputGroupDemo() {
5
return (
6
<InputGroup class="max-w-xs">
7
<InputGroupInput placeholder="Search..." />
8
<InputGroupAddon>
9
<SearchIcon />
10
</InputGroupAddon>
11
<InputGroupAddon align="inline-end">12 results</InputGroupAddon>
12
</InputGroup>
13
);
14
}

Installation

pnpm dlx shadcn@latest add @zaidan/input-group
npx shadcn@latest add @zaidan/input-group
yarn dlx shadcn@latest add @zaidan/input-group
bunx --bun shadcn@latest add @zaidan/input-group

Usage

1
import {
2
InputGroup,
3
InputGroupAddon,
4
InputGroupButton,
5
InputGroupInput,
6
InputGroupText,
7
InputGroupTextarea,
8
} from "~/components/ui/input-group";
1
<InputGroup>
2
<InputGroupInput placeholder="Search..." />
3
<InputGroupAddon>
4
<SearchIcon />
5
</InputGroupAddon>
6
</InputGroup>

Composition

Use the following composition to build an InputGroup:

InputGroup
├── InputGroupInput or InputGroupTextarea
├── InputGroupAddon
├── InputGroupButton
└── InputGroupText

Align

Use the align prop on InputGroupAddon to position the addon relative to the input.

inline-start

Use align="inline-start" to position the addon at the start of the input. This is the default.

Icon positioned at the start.

1
import { SearchIcon } from "lucide-solid";
2
import { Field, FieldDescription, FieldLabel } from "~/components/ui/field";
3
import { InputGroup, InputGroupAddon, InputGroupInput } from "~/components/ui/input-group";
4
5
export default function InputGroupInlineStart() {
6
return (
7
<Field class="max-w-sm">
8
<FieldLabel for="inline-start-input">Input</FieldLabel>
9
<InputGroup>
10
<InputGroupInput id="inline-start-input" placeholder="Search..." />
11
<InputGroupAddon align="inline-start">
12
<SearchIcon class="text-muted-foreground" />
13
</InputGroupAddon>
14
</InputGroup>
15
<FieldDescription>Icon positioned at the start.</FieldDescription>
16
</Field>
17
);
18
}

inline-end

Use align="inline-end" to position the addon at the end of the input.

Icon positioned at the end.

1
import { EyeOffIcon } from "lucide-solid";
2
import { Field, FieldDescription, FieldLabel } from "~/components/ui/field";
3
import { InputGroup, InputGroupAddon, InputGroupInput } from "~/components/ui/input-group";
4
5
export default function InputGroupInlineEnd() {
6
return (
7
<Field class="max-w-sm">
8
<FieldLabel for="inline-end-input">Input</FieldLabel>
9
<InputGroup>
10
<InputGroupInput id="inline-end-input" type="password" placeholder="Enter password" />
11
<InputGroupAddon align="inline-end">
12
<EyeOffIcon />
13
</InputGroupAddon>
14
</InputGroup>
15
<FieldDescription>Icon positioned at the end.</FieldDescription>
16
</Field>
17
);
18
}

block-start

Use align="block-start" to position the addon above the input.

Full Name

Header positioned above the input.

script.js

Header positioned above the textarea.

1
import { CopyIcon, FileCodeIcon } from "lucide-solid";
2
import { Field, FieldDescription, FieldGroup, FieldLabel } from "~/components/ui/field";
3
import {
4
InputGroup,
5
InputGroupAddon,
6
InputGroupButton,
7
InputGroupInput,
8
InputGroupText,
9
InputGroupTextarea,
10
} from "~/components/ui/input-group";
11
12
export default function InputGroupBlockStart() {
13
return (
14
<FieldGroup class="max-w-sm">
15
<Field>
16
<FieldLabel for="block-start-input">Input</FieldLabel>
17
<InputGroup class="h-auto">
18
<InputGroupInput id="block-start-input" placeholder="Enter your name" />
19
<InputGroupAddon align="block-start">
20
<InputGroupText>Full Name</InputGroupText>
21
</InputGroupAddon>
22
</InputGroup>
23
<FieldDescription>Header positioned above the input.</FieldDescription>
24
</Field>
25
<Field>
26
<FieldLabel for="block-start-textarea">Textarea</FieldLabel>
27
<InputGroup>
28
<InputGroupTextarea
29
id="block-start-textarea"
30
placeholder="console.log('Hello, world!');"
31
class="font-mono text-sm"
32
/>
33
<InputGroupAddon align="block-start">
34
<FileCodeIcon class="text-muted-foreground" />
35
<InputGroupText class="font-mono">script.js</InputGroupText>
36
<InputGroupButton size="icon-xs" class="ml-auto" aria-label="Copy">
37
<CopyIcon />
38
</InputGroupButton>
39
</InputGroupAddon>
40
</InputGroup>
41
<FieldDescription>Header positioned above the textarea.</FieldDescription>
42
</Field>
43
</FieldGroup>
44
);
45
}

block-end

Use align="block-end" to position the addon below the input.

USD

Footer positioned below the input.

0/280

Footer positioned below the textarea.

1
import { Field, FieldDescription, FieldGroup, FieldLabel } from "~/components/ui/field";
2
import {
3
InputGroup,
4
InputGroupAddon,
5
InputGroupButton,
6
InputGroupInput,
7
InputGroupText,
8
InputGroupTextarea,
9
} from "~/components/ui/input-group";
10
11
export default function InputGroupBlockEnd() {
12
return (
13
<FieldGroup class="max-w-sm">
14
<Field>
15
<FieldLabel for="block-end-input">Input</FieldLabel>
16
<InputGroup class="h-auto">
17
<InputGroupInput id="block-end-input" placeholder="Enter amount" />
18
<InputGroupAddon align="block-end">
19
<InputGroupText>USD</InputGroupText>
20
</InputGroupAddon>
21
</InputGroup>
22
<FieldDescription>Footer positioned below the input.</FieldDescription>
23
</Field>
24
<Field>
25
<FieldLabel for="block-end-textarea">Textarea</FieldLabel>
26
<InputGroup>
27
<InputGroupTextarea id="block-end-textarea" placeholder="Write a comment..." />
28
<InputGroupAddon align="block-end">
29
<InputGroupText>0/280</InputGroupText>
30
<InputGroupButton variant="default" size="sm" class="ml-auto">
31
Post
32
</InputGroupButton>
33
</InputGroupAddon>
34
</InputGroup>
35
<FieldDescription>Footer positioned below the textarea.</FieldDescription>
36
</Field>
37
</FieldGroup>
38
);
39
}

Icon

1
import { CheckIcon, CreditCardIcon, InfoIcon, MailIcon, SearchIcon, StarIcon } from "lucide-solid";
2
import { InputGroup, InputGroupAddon, InputGroupInput } from "~/components/ui/input-group";
3
4
export default function InputGroupIcon() {
5
return (
6
<div class="grid w-full max-w-sm gap-6">
7
<InputGroup>
8
<InputGroupInput placeholder="Search..." />
9
<InputGroupAddon>
10
<SearchIcon />
11
</InputGroupAddon>
12
</InputGroup>
13
<InputGroup>
14
<InputGroupInput type="email" placeholder="Enter your email" />
15
<InputGroupAddon>
16
<MailIcon />
17
</InputGroupAddon>
18
</InputGroup>
19
<InputGroup>
20
<InputGroupInput placeholder="Card number" />
21
<InputGroupAddon>
22
<CreditCardIcon />
23
</InputGroupAddon>
24
<InputGroupAddon align="inline-end">
25
<CheckIcon />
26
</InputGroupAddon>
27
</InputGroup>
28
<InputGroup>
29
<InputGroupInput placeholder="Card number" />
30
<InputGroupAddon align="inline-end">
31
<StarIcon />
32
<InfoIcon />
33
</InputGroupAddon>
34
</InputGroup>
35
</div>
36
);
37
}

Text

$
USD
https://
.com
@company.com
120 characters left
1
import {
2
InputGroup,
3
InputGroupAddon,
4
InputGroupInput,
5
InputGroupText,
6
InputGroupTextarea,
7
} from "~/components/ui/input-group";
8
9
export default function InputGroupTextExample() {
10
return (
11
<div class="grid w-full max-w-sm gap-6">
12
<InputGroup>
13
<InputGroupAddon>
14
<InputGroupText>$</InputGroupText>
15
</InputGroupAddon>
16
<InputGroupInput placeholder="0.00" />
17
<InputGroupAddon align="inline-end">
18
<InputGroupText>USD</InputGroupText>
19
</InputGroupAddon>
20
</InputGroup>
21
<InputGroup>
22
<InputGroupAddon>
23
<InputGroupText>https://</InputGroupText>
24
</InputGroupAddon>
25
<InputGroupInput placeholder="example.com" class="pl-0.5!" />
26
<InputGroupAddon align="inline-end">
27
<InputGroupText>.com</InputGroupText>
28
</InputGroupAddon>
29
</InputGroup>
30
<InputGroup>
31
<InputGroupInput placeholder="Enter your username" />
32
<InputGroupAddon align="inline-end">
33
<InputGroupText>@company.com</InputGroupText>
34
</InputGroupAddon>
35
</InputGroup>
36
<InputGroup>
37
<InputGroupTextarea placeholder="Enter your message" />
38
<InputGroupAddon align="block-end">
39
<InputGroupText class="text-xs text-muted-foreground">120 characters left</InputGroupText>
40
</InputGroupAddon>
41
</InputGroup>
42
</div>
43
);
44
}

Button

https://
1
import { CheckIcon, CopyIcon, InfoIcon, StarIcon } from "lucide-solid";
2
import { createSignal } from "solid-js";
3
import {
4
InputGroup,
5
InputGroupAddon,
6
InputGroupButton,
7
InputGroupInput,
8
} from "~/components/ui/input-group";
9
import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover";
10
11
export default function InputGroupButtonExample() {
12
const [copied, setCopied] = createSignal(false);
13
const [favorite, setFavorite] = createSignal(false);
14
15
const copy = async () => {
16
await navigator.clipboard?.writeText("https://x.com/shadcn");
17
setCopied(true);
18
};
19
20
return (
21
<div class="grid w-full max-w-sm gap-6">
22
<InputGroup>
23
<InputGroupInput placeholder="https://x.com/shadcn" readonly />
24
<InputGroupAddon align="inline-end">
25
<InputGroupButton aria-label="Copy" title="Copy" size="icon-xs" onClick={copy}>
26
{copied() ? <CheckIcon /> : <CopyIcon />}
27
</InputGroupButton>
28
</InputGroupAddon>
29
</InputGroup>
30
<InputGroup class="[--radius:9999px]">
31
<Popover placement="bottom-start">
32
<PopoverTrigger as={InputGroupAddon}>
33
<InputGroupButton
34
variant="secondary"
35
size="icon-xs"
36
aria-label="Connection information"
37
>
38
<InfoIcon />
39
</InputGroupButton>
40
</PopoverTrigger>
41
<PopoverContent class="flex flex-col gap-1 rounded-xl text-sm">
42
<p class="font-medium">Your connection is not secure.</p>
43
<p>You should not enter any sensitive information on this site.</p>
44
</PopoverContent>
45
</Popover>
46
<InputGroupAddon class="pl-1.5 text-muted-foreground">https://</InputGroupAddon>
47
<InputGroupInput id="input-secure-19" />
48
<InputGroupAddon align="inline-end">
49
<InputGroupButton
50
onClick={() => setFavorite((value) => !value)}
51
size="icon-xs"
52
aria-label="Toggle favorite"
53
aria-pressed={favorite()}
54
>
55
<StarIcon
56
class="data-[favorite=true]:fill-blue-600 data-[favorite=true]:stroke-blue-600"
57
data-favorite={favorite()}
58
/>
59
</InputGroupButton>
60
</InputGroupAddon>
61
</InputGroup>
62
<InputGroup>
63
<InputGroupInput placeholder="Type to search..." />
64
<InputGroupAddon align="inline-end">
65
<InputGroupButton variant="secondary">Search</InputGroupButton>
66
</InputGroupAddon>
67
</InputGroup>
68
</div>
69
);
70
}

Kbd

⌘K
1
import { SearchIcon } from "lucide-solid";
2
import { InputGroup, InputGroupAddon, InputGroupInput } from "~/components/ui/input-group";
3
import { Kbd } from "~/components/ui/kbd";
4
5
export default function InputGroupKbd() {
6
return (
7
<InputGroup class="max-w-sm">
8
<InputGroupInput placeholder="Search..." />
9
<InputGroupAddon>
10
<SearchIcon class="text-muted-foreground" />
11
</InputGroupAddon>
12
<InputGroupAddon align="inline-end">
13
<Kbd>⌘K</Kbd>
14
</InputGroupAddon>
15
</InputGroup>
16
);
17
}

Dropdown

1
import { ChevronDownIcon, MoreHorizontalIcon } from "lucide-solid";
2
import {
3
DropdownMenu,
4
DropdownMenuContent,
5
DropdownMenuGroup,
6
DropdownMenuItem,
7
DropdownMenuTrigger,
8
} from "~/components/ui/dropdown-menu";
9
import {
10
InputGroup,
11
InputGroupAddon,
12
InputGroupButton,
13
InputGroupInput,
14
} from "~/components/ui/input-group";
15
16
export default function InputGroupDropdown() {
17
return (
18
<div class="grid w-full max-w-sm gap-4">
19
<InputGroup>
20
<InputGroupInput placeholder="Enter file name" />
21
<InputGroupAddon align="inline-end">
22
<DropdownMenu placement="bottom-end" gutter={8}>
23
<DropdownMenuTrigger
24
as={InputGroupButton}
25
variant="ghost"
26
aria-label="More"
27
size="icon-xs"
28
>
29
<MoreHorizontalIcon />
30
</DropdownMenuTrigger>
31
<DropdownMenuContent>
32
<DropdownMenuGroup>
33
<DropdownMenuItem>Settings</DropdownMenuItem>
34
<DropdownMenuItem>Copy path</DropdownMenuItem>
35
<DropdownMenuItem>Open location</DropdownMenuItem>
36
</DropdownMenuGroup>
37
</DropdownMenuContent>
38
</DropdownMenu>
39
</InputGroupAddon>
40
</InputGroup>
41
<InputGroup>
42
<InputGroupInput placeholder="Enter search query" />
43
<InputGroupAddon align="inline-end">
44
<DropdownMenu placement="bottom-end" gutter={8}>
45
<DropdownMenuTrigger as={InputGroupButton} variant="ghost" class="pr-1.5! text-xs">
46
Search In... <ChevronDownIcon class="size-3" />
47
</DropdownMenuTrigger>
48
<DropdownMenuContent>
49
<DropdownMenuGroup>
50
<DropdownMenuItem>Documentation</DropdownMenuItem>
51
<DropdownMenuItem>Blog Posts</DropdownMenuItem>
52
<DropdownMenuItem>Changelog</DropdownMenuItem>
53
</DropdownMenuGroup>
54
</DropdownMenuContent>
55
</DropdownMenu>
56
</InputGroupAddon>
57
</InputGroup>
58
</div>
59
);
60
}

Spinner

Saving...
Please wait...
1
import { LoaderCircleIcon } from "lucide-solid";
2
import {
3
InputGroup,
4
InputGroupAddon,
5
InputGroupInput,
6
InputGroupText,
7
} from "~/components/ui/input-group";
8
import { Spinner } from "~/components/ui/spinner";
9
10
export default function InputGroupSpinner() {
11
return (
12
<div class="grid w-full max-w-sm gap-4">
13
<InputGroup>
14
<InputGroupInput placeholder="Searching..." />
15
<InputGroupAddon align="inline-end">
16
<Spinner />
17
</InputGroupAddon>
18
</InputGroup>
19
<InputGroup>
20
<InputGroupInput placeholder="Processing..." />
21
<InputGroupAddon>
22
<Spinner />
23
</InputGroupAddon>
24
</InputGroup>
25
<InputGroup>
26
<InputGroupInput placeholder="Saving changes..." />
27
<InputGroupAddon align="inline-end">
28
<InputGroupText>Saving...</InputGroupText>
29
<Spinner />
30
</InputGroupAddon>
31
</InputGroup>
32
<InputGroup>
33
<InputGroupInput placeholder="Refreshing data..." />
34
<InputGroupAddon>
35
<LoaderCircleIcon class="animate-spin" />
36
</InputGroupAddon>
37
<InputGroupAddon align="inline-end">
38
<InputGroupText class="text-muted-foreground">Please wait...</InputGroupText>
39
</InputGroupAddon>
40
</InputGroup>
41
</div>
42
);
43
}

Textarea

Line 1, Column 1
script.js
1
import { BracesIcon, CopyIcon, CornerDownLeftIcon, RefreshCwIcon } from "lucide-solid";
2
import {
3
InputGroup,
4
InputGroupAddon,
5
InputGroupButton,
6
InputGroupText,
7
InputGroupTextarea,
8
} from "~/components/ui/input-group";
9
10
export default function InputGroupTextareaExample() {
11
return (
12
<div class="grid w-full max-w-md gap-4">
13
<InputGroup>
14
<InputGroupTextarea
15
id="textarea-code-32"
16
placeholder="console.log('Hello, world!');"
17
class="min-h-[200px]"
18
/>
19
<InputGroupAddon align="block-end" class="border-t">
20
<InputGroupText>Line 1, Column 1</InputGroupText>
21
<InputGroupButton size="sm" class="ml-auto" variant="default">
22
Run <CornerDownLeftIcon />
23
</InputGroupButton>
24
</InputGroupAddon>
25
<InputGroupAddon align="block-start" class="border-b">
26
<InputGroupText class="font-mono font-medium">
27
<BracesIcon />
28
script.js
29
</InputGroupText>
30
<InputGroupButton class="ml-auto" size="icon-xs" aria-label="Refresh">
31
<RefreshCwIcon />
32
</InputGroupButton>
33
<InputGroupButton variant="ghost" size="icon-xs" aria-label="Copy">
34
<CopyIcon />
35
</InputGroupButton>
36
</InputGroupAddon>
37
</InputGroup>
38
</div>
39
);
40
}

Custom Input

Add the data-slot="input-group-control" attribute to a custom input for automatic focus state handling.

1
import { InputGroup, InputGroupAddon, InputGroupButton } from "~/components/ui/input-group";
2
3
export default function InputGroupCustom() {
4
const resize = (event: InputEvent & { currentTarget: HTMLTextAreaElement }) => {
5
const textarea = event.currentTarget;
6
textarea.style.height = "auto";
7
textarea.style.height = `${textarea.scrollHeight}px`;
8
};
9
10
return (
11
<div class="grid w-full max-w-sm gap-6">
12
<InputGroup>
13
<textarea
14
data-slot="input-group-control"
15
class="flex field-sizing-content min-h-16 w-full resize-none rounded-md bg-transparent px-3 py-2.5 text-base transition-[color,box-shadow] outline-none md:text-sm"
16
placeholder="Autoresize textarea..."
17
onInput={resize}
18
/>
19
<InputGroupAddon align="block-end">
20
<InputGroupButton class="ml-auto" size="sm" variant="default">
21
Submit
22
</InputGroupButton>
23
</InputGroupAddon>
24
</InputGroup>
25
</div>
26
);
27
}

API Reference

InputGroup

The main component that wraps inputs and addons.

PropTypeDefault
classstring
<InputGroup>
<InputGroupInput />
<InputGroupAddon />
</InputGroup>

InputGroupAddon

Displays icons, text, buttons, or other content alongside inputs. Place the addon after its input in the DOM and use align to position it visually.

PropTypeDefault
align"inline-start" | "inline-end" | "block-start" | "block-end""inline-start"
classstring
<InputGroupAddon align="inline-end">
<SearchIcon />
</InputGroupAddon>

For <InputGroupInput />, use the inline-start or inline-end alignment. For <InputGroupTextarea />, use the block-start or block-end alignment.

InputGroupButton

Displays buttons within input groups.

PropTypeDefault
size"xs" | "icon-xs" | "sm" | "icon-sm""xs"
variant"default" | "destructive" | "outline" | "secondary" | "ghost" | "link""ghost"
classstring
<InputGroupButton>Button</InputGroupButton>

InputGroupInput

Replacement for <Input /> when building input groups. It applies input-group styles and data-slot="input-group-control" for focus state handling.

PropTypeDefault
classstring

InputGroupTextarea

Replacement for <Textarea /> when building input groups. It applies textarea-group styles and data-slot="input-group-control" for focus state handling.

PropTypeDefault
classstring

On This Page

  • Installation
  • Usage
  • Composition
  • Align
    • inline-start
    • inline-end
    • block-start
    • block-end
  • Icon
  • Text
  • Button
  • Kbd
  • Dropdown
  • Spinner
  • Textarea
  • Custom Input
  • API Reference
    • InputGroup
    • InputGroupAddon
    • InputGroupButton
    • InputGroupInput
    • InputGroupTextarea
Built by Kevin Abatan. The source code is available on GitHub.