<script lang="ts">
import IconCheck from "@tabler/icons-svelte/icons/check";
import IconInfoCircle from "@tabler/icons-svelte/icons/info-circle";
import IconPlus from "@tabler/icons-svelte/icons/plus";
import * as InputGroup from "$lib/components/ui/input-group/index.js";
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";
import * as Tooltip from "$lib/components/ui/tooltip/index.js";
import { Separator } from "$lib/components/ui/separator/index.js";
import SearchIcon from "@lucide/svelte/icons/search";
import ArrowUpIcon from "@lucide/svelte/icons/arrow-up";
</script>
<div class="grid w-full max-w-sm gap-6">
<InputGroup.Root>
<InputGroup.Input placeholder="Search..." />
<InputGroup.Addon>
<SearchIcon />
</InputGroup.Addon>
<InputGroup.Addon align="inline-end">12 results</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root>
<InputGroup.Input placeholder="example.com" class="!ps-1" />
<InputGroup.Addon>
<InputGroup.Text>https://</InputGroup.Text>
</InputGroup.Addon>
<InputGroup.Addon align="inline-end">
<Tooltip.Root>
<Tooltip.Trigger>
{#snippet child({ props })}
<InputGroup.Button {...props} class="rounded-full" size="icon-xs">
<IconInfoCircle />
</InputGroup.Button>
{/snippet}
</Tooltip.Trigger>
<Tooltip.Content>This is content in a tooltip.</Tooltip.Content>
</Tooltip.Root>
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root>
<InputGroup.Textarea placeholder="Ask, Search or Chat..." />
<InputGroup.Addon align="block-end">
<InputGroup.Button variant="outline" class="rounded-full" size="icon-xs">
<IconPlus />
</InputGroup.Button>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<InputGroup.Button {...props} variant="ghost"
>Auto</InputGroup.Button
>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content
side="top"
align="start"
class="[--radius:0.95rem]"
>
<DropdownMenu.Item>Auto</DropdownMenu.Item>
<DropdownMenu.Item>Agent</DropdownMenu.Item>
<DropdownMenu.Item>Manual</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
<InputGroup.Text class="ms-auto">52% used</InputGroup.Text>
<Separator orientation="vertical" class="!h-4" />
<InputGroup.Button
variant="default"
class="rounded-full"
size="icon-xs"
disabled
>
<ArrowUpIcon />
<span class="sr-only">Send</span>
</InputGroup.Button>
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root>
<InputGroup.Input placeholder="@shadcn" />
<InputGroup.Addon align="inline-end">
<div
class="bg-primary text-primary-foreground flex size-4 items-center justify-center rounded-full"
>
<IconCheck class="size-3" />
</div>
</InputGroup.Addon>
</InputGroup.Root>
</div> Installation
pnpm dlx shadcn-svelte@latest add input-group Install @lucide/svelte:
pnpm add @lucide/svelte -D Copy and paste the following code into your project.
import Root from './input-group.svelte';
import Addon from './input-group-addon.svelte';
import Button from './input-group-button.svelte';
import Input from './input-group-input.svelte';
import Text from './input-group-text.svelte';
import Textarea from './input-group-textarea.svelte';
export {
Root,
Addon,
Button,
Input,
Text,
Textarea,
//
Root as InputGroup,
Addon as InputGroupAddon,
Button as InputGroupButton,
Input as InputGroupInput,
Text as InputGroupText,
Textarea as InputGroupTextarea
};
<script lang="ts" module>
import { tv, type VariantProps } from 'tailwind-variants';
export const inputGroupAddonVariants = tv({
base: "text-zinc-500 **:data-[slot=kbd]:bg-background h-auto gap-2 py-2 font-mono text-xs font-semibold tracking-widest uppercase group-data-[disabled=true]/input-group:opacity-50 **:data-[slot=kbd]:rounded-none **:data-[slot=kbd]:px-1.5 [&>svg:not([class*='size-'])]:size-3.5 flex cursor-text items-center justify-center select-none",
variants: {
align: {
'inline-start':
'cn-input-group-addon-align-inline-start order-first border-r border-zinc-800 px-3',
'inline-end':
'cn-input-group-addon-align-inline-end order-last border-l border-zinc-800 px-3',
'block-start':
'border-b border-zinc-800 px-3 pt-3 group-has-[>input]/input-group:pt-3.5 [.border-b]:pb-3.5 order-first w-full justify-start',
'block-end':
'border-t border-zinc-800 px-3 pb-3 group-has-[>input]/input-group:pb-3.5 [.border-t]:pt-3.5 order-last w-full justify-start'
}
},
defaultVariants: {
align: 'inline-start'
}
});
export type InputGroupAddonAlign = VariantProps<typeof inputGroupAddonVariants>['align'];
</script>
<script lang="ts">
import { cn, type WithElementRef } from '$UTILS$.js';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
align = 'inline-start',
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
align?: InputGroupAddonAlign;
} = $props();
</script>
<div
bind:this={ref}
role="group"
data-slot="input-group-addon"
data-align={align}
class={cn(inputGroupAddonVariants({ align }), className)}
onclick={(e) => {
if ((e.target as HTMLElement).closest('button')) {
return;
}
e.currentTarget.parentElement?.querySelector('input')?.focus();
}}
{...restProps}
>
{@render children?.()}
</div>
<script lang="ts" module>
import { tv, type VariantProps } from 'tailwind-variants';
const inputGroupButtonVariants = tv({
base: 'gap-2 rounded-full font-mono text-xs flex items-center shadow-none',
variants: {
size: {
xs: "h-6 gap-1 px-2 [&>svg:not([class*='size-'])]:size-3.5",
sm: 'cn-input-group-button-size-sm',
'icon-xs': 'size-6 p-0 text-xs has-[>svg]:p-0',
'icon-sm': 'size-8 p-0 has-[>svg]:p-0'
}
},
defaultVariants: {
size: 'xs'
}
});
export type InputGroupButtonSize = VariantProps<typeof inputGroupButtonVariants>['size'];
</script>
<script lang="ts">
import { cn } from '$UTILS$.js';
import type { ComponentProps } from 'svelte';
import { Button } from '$UI$/button/index.js';
let {
ref = $bindable(null),
class: className,
children,
type = 'button',
variant = 'ghost',
size = 'xs',
...restProps
}: Omit<ComponentProps<typeof Button>, 'href' | 'size'> & {
size?: InputGroupButtonSize;
} = $props();
</script>
<Button
bind:ref
{type}
data-size={size}
{variant}
class={cn(inputGroupButtonVariants({ size }), className)}
{...restProps}
>
{@render children?.()}
</Button>
<script lang="ts">
import { cn } from '$UTILS$.js';
import type { ComponentProps } from 'svelte';
import { Input } from '$UI$/input/index.js';
let {
ref = $bindable(null),
value = $bindable(),
class: className,
...props
}: ComponentProps<typeof Input> = $props();
</script>
<Input
bind:ref
data-slot="input-group-control"
class={cn(
'border-0 bg-transparent px-3 ring-0 group-has-[>[data-align=inline-end]]/input-group:pr-2 group-has-[>[data-align=inline-start]]/input-group:pl-2 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent flex-1',
className
)}
bind:value
{...props}
/>
<script lang="ts">
import { cn, type WithElementRef } from '$UTILS$.js';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
</script>
<span
bind:this={ref}
data-slot="input-group-text"
class={cn(
"gap-2 font-mono text-xs font-semibold tracking-widest text-zinc-500 uppercase [&_svg:not([class*='size-'])]:size-3.5 flex items-center [&_svg]:pointer-events-none",
className
)}
{...restProps}
>
{@render children?.()}
</span>
<script lang="ts">
import { cn } from '$UTILS$.js';
import { Textarea } from '$UI$/textarea/index.js';
import type { ComponentProps } from 'svelte';
let {
ref = $bindable(null),
value = $bindable(),
class: className,
...props
}: ComponentProps<typeof Textarea> = $props();
</script>
<Textarea
bind:ref
data-slot="input-group-control"
class={cn(
'border-0 bg-transparent px-3 py-2.5 ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent flex-1 resize-none',
className
)}
bind:value
{...props}
/>
<script lang="ts">
import { cn, type WithElementRef } from '$UTILS$.js';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...props
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="input-group"
role="group"
class={cn(
'group/input-group border-zinc-800 bg-zinc-900 has-[[data-slot=input-group-control]:focus-visible]:border-zinc-300 has-[[data-slot=input-group-control]:focus-visible]:ring-2 has-[[data-slot=input-group-control]:focus-visible]:ring-zinc-300/25 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-2 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 dark:has-[[data-slot][aria-invalid=true]]:border-destructive/50 h-9 rounded-none border transition-[color,border-color,box-shadow] in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-data-[align=block-end]:rounded-none has-data-[align=block-start]:rounded-none has-[textarea]:rounded-none has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 relative flex w-full min-w-0 items-center outline-none has-[>textarea]:h-auto',
className
)}
{...props}
>
{@render children?.()}
</div>
Usage
<script lang="ts">
import * as InputGroup from '$lib/components/ui/input-group/index.js';
import SearchIcon from '@lucide/svelte/icons/search';
</script> <InputGroup.Root>
<InputGroup.Input placeholder="Search..." />
<InputGroup.Addon>
<SearchIcon />
</InputGroup.Addon>
<InputGroup.Addon align="inline-end">
<InputGroup.Button>Search</InputGroup.Button>
</InputGroup.Addon>
</InputGroup.Root> Examples
Icon
<script lang="ts">
import * as InputGroup from "$lib/components/ui/input-group/index.js";
import CheckIcon from "@lucide/svelte/icons/check";
import CreditCardIcon from "@lucide/svelte/icons/credit-card";
import InfoIcon from "@lucide/svelte/icons/info";
import MailIcon from "@lucide/svelte/icons/mail";
import SearchIcon from "@lucide/svelte/icons/search";
import StarIcon from "@lucide/svelte/icons/star";
</script>
<div class="grid w-full max-w-sm gap-6">
<InputGroup.Root>
<InputGroup.Input placeholder="Search..." />
<InputGroup.Addon>
<SearchIcon />
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root>
<InputGroup.Input type="email" placeholder="Enter your email" />
<InputGroup.Addon>
<MailIcon />
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root>
<InputGroup.Input placeholder="Card number" />
<InputGroup.Addon>
<CreditCardIcon />
</InputGroup.Addon>
<InputGroup.Addon align="inline-end">
<CheckIcon />
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root>
<InputGroup.Input placeholder="Card number" />
<InputGroup.Addon align="inline-end">
<StarIcon />
<InfoIcon />
</InputGroup.Addon>
</InputGroup.Root>
</div> Text
Display additional text information alongside inputs.
<script lang="ts">
import * as InputGroup from "$lib/components/ui/input-group/index.js";
</script>
<div class="grid w-full max-w-sm gap-6">
<InputGroup.Root>
<InputGroup.Addon>
<InputGroup.Text>$</InputGroup.Text>
</InputGroup.Addon>
<InputGroup.Input placeholder="0.00" />
<InputGroup.Addon align="inline-end">
<InputGroup.Text>USD</InputGroup.Text>
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root>
<InputGroup.Addon>
<InputGroup.Text>https://</InputGroup.Text>
</InputGroup.Addon>
<InputGroup.Input placeholder="example.com" class="!ps-0.5" />
<InputGroup.Addon align="inline-end">
<InputGroup.Text>.com</InputGroup.Text>
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root>
<InputGroup.Input placeholder="Enter your username" />
<InputGroup.Addon align="inline-end">
<InputGroup.Text>@company.com</InputGroup.Text>
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root>
<InputGroup.Textarea placeholder="Enter your message" />
<InputGroup.Addon align="block-end">
<InputGroup.Text class="text-muted-foreground text-xs"
>120 characters left</InputGroup.Text
>
</InputGroup.Addon>
</InputGroup.Root>
</div> Button
Add buttons to perform actions within the input group.
<script lang="ts">
import IconCheck from "@tabler/icons-svelte/icons/check";
import IconCopy from "@tabler/icons-svelte/icons/copy";
import IconInfoCircle from "@tabler/icons-svelte/icons/info-circle";
import IconStar from "@tabler/icons-svelte/icons/star";
import * as InputGroup from "$lib/components/ui/input-group/index.js";
import * as Popover from "$lib/components/ui/popover/index.js";
import { UseClipboard } from "$lib/hooks/use-clipboard.svelte.js";
let isFavorite = $state(false);
const clipboard = new UseClipboard();
</script>
<div class="grid w-full max-w-sm gap-6">
<InputGroup.Root>
<InputGroup.Input placeholder="https://x.com/shadcn" readonly />
<InputGroup.Addon align="inline-end">
<InputGroup.Button
aria-label="Copy"
title="Copy"
size="icon-xs"
onclick={() => clipboard.copy("https://x.com/shadcn")}
>
{#if clipboard.copied}
<IconCheck />
{:else}
<IconCopy />
{/if}
</InputGroup.Button>
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root class="[--radius:9999px]">
<Popover.Root>
<Popover.Trigger>
{#snippet child({ props })}
<InputGroup.Addon>
<InputGroup.Button {...props} variant="secondary" size="icon-xs">
<IconInfoCircle />
</InputGroup.Button>
</InputGroup.Addon>
{/snippet}
</Popover.Trigger>
<Popover.Content
align="start"
class="flex flex-col gap-1 rounded-xl text-sm"
>
<p class="font-medium">Your connection is not secure.</p>
<p>You should not enter any sensitive information on this site.</p>
</Popover.Content>
</Popover.Root>
<InputGroup.Addon class="text-muted-foreground ps-1.5">
<InputGroup.Text>https://</InputGroup.Text>
</InputGroup.Addon>
<InputGroup.Input />
<InputGroup.Addon align="inline-end">
<InputGroup.Button
onclick={() => (isFavorite = !isFavorite)}
size="icon-xs"
>
<IconStar class={isFavorite ? "fill-blue-600 stroke-blue-600" : ""} />
</InputGroup.Button>
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root>
<InputGroup.Input placeholder="Type to search..." />
<InputGroup.Addon align="inline-end">
<InputGroup.Button variant="secondary">Search</InputGroup.Button>
</InputGroup.Addon>
</InputGroup.Root>
</div> Tooltip
Add tooltips to provide additional context or help.
<script lang="ts">
import * as InputGroup from "$lib/components/ui/input-group/index.js";
import * as Tooltip from "$lib/components/ui/tooltip/index.js";
import HelpCircleIcon from "@lucide/svelte/icons/help-circle";
import InfoIcon from "@lucide/svelte/icons/info";
</script>
<div class="grid w-full max-w-sm gap-4">
<InputGroup.Root>
<InputGroup.Input placeholder="Enter password" type="password" />
<InputGroup.Addon align="inline-end">
<Tooltip.Root>
<Tooltip.Trigger>
{#snippet child({ props })}
<InputGroup.Button
{...props}
variant="ghost"
aria-label="Info"
size="icon-xs"
>
<InfoIcon />
</InputGroup.Button>
{/snippet}
</Tooltip.Trigger>
<Tooltip.Content>
<p>Password must be at least 8 characters</p>
</Tooltip.Content>
</Tooltip.Root>
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root>
<InputGroup.Input placeholder="Your email address" />
<InputGroup.Addon align="inline-end">
<Tooltip.Root>
<Tooltip.Trigger>
{#snippet child({ props })}
<InputGroup.Button
{...props}
variant="ghost"
aria-label="Help"
size="icon-xs"
>
<HelpCircleIcon />
</InputGroup.Button>
{/snippet}
</Tooltip.Trigger>
<Tooltip.Content>
<p>We'll use this to send you notifications</p>
</Tooltip.Content>
</Tooltip.Root>
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root>
<InputGroup.Input placeholder="Enter API key" />
<Tooltip.Root>
<Tooltip.Trigger>
{#snippet child({ props })}
<InputGroup.Addon>
<InputGroup.Button
{...props}
variant="ghost"
aria-label="Help"
size="icon-xs"
>
<HelpCircleIcon />
</InputGroup.Button>
</InputGroup.Addon>
{/snippet}
</Tooltip.Trigger>
<Tooltip.Content side="left">
<p>Click for help with API keys</p>
</Tooltip.Content>
</Tooltip.Root>
</InputGroup.Root>
</div> Textarea
Input groups also work with textarea components. Use block-start or block-end for alignment.
<script lang="ts">
import IconBrandJavascript from "@tabler/icons-svelte/icons/brand-javascript";
import IconCopy from "@tabler/icons-svelte/icons/copy";
import IconCornerDownLeft from "@tabler/icons-svelte/icons/corner-down-left";
import IconRefresh from "@tabler/icons-svelte/icons/refresh";
import * as InputGroup from "$lib/components/ui/input-group/index.js";
</script>
<div class="grid w-full max-w-md gap-4">
<InputGroup.Root>
<InputGroup.Addon align="block-start" class="border-b">
<InputGroup.Text class="font-mono font-medium">
<IconBrandJavascript />
script.js
</InputGroup.Text>
<InputGroup.Button class="ms-auto" size="icon-xs">
<IconRefresh />
</InputGroup.Button>
<InputGroup.Button variant="ghost" size="icon-xs">
<IconCopy />
</InputGroup.Button>
</InputGroup.Addon>
<InputGroup.Textarea
placeholder="console.log('Hello, world!');"
class="min-h-[200px]"
/>
<InputGroup.Addon align="block-end" class="border-t">
<InputGroup.Text>Line 1, Column 1</InputGroup.Text>
<InputGroup.Button size="sm" class="ms-auto" variant="default">
Run <IconCornerDownLeft />
</InputGroup.Button>
</InputGroup.Addon>
</InputGroup.Root>
</div> Spinner
Show loading indicators while processing input.
<script lang="ts">
import * as InputGroup from "$lib/components/ui/input-group/index.js";
import { Spinner } from "$lib/components/ui/spinner/index.js";
import LoaderIcon from "@lucide/svelte/icons/loader";
</script>
<div class="grid w-full max-w-sm gap-4">
<InputGroup.Root data-disabled>
<InputGroup.Input placeholder="Searching..." disabled />
<InputGroup.Addon align="inline-end">
<Spinner />
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root data-disabled>
<InputGroup.Input placeholder="Processing..." disabled />
<InputGroup.Addon>
<Spinner />
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root data-disabled>
<InputGroup.Input placeholder="Saving changes..." disabled />
<InputGroup.Addon align="inline-end">
<InputGroup.Text>Saving...</InputGroup.Text>
<Spinner />
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root data-disabled>
<InputGroup.Input placeholder="Refreshing data..." disabled />
<InputGroup.Addon>
<LoaderIcon class="animate-spin" />
</InputGroup.Addon>
<InputGroup.Addon align="inline-end">
<InputGroup.Text class="text-muted-foreground"
>Please wait...</InputGroup.Text
>
</InputGroup.Addon>
</InputGroup.Root>
</div> Label
Add labels within input groups to improve accessibility.
<script lang="ts">
import * as InputGroup from "$lib/components/ui/input-group/index.js";
import * as Label from "$lib/components/ui/label/index.js";
import * as Tooltip from "$lib/components/ui/tooltip/index.js";
import InfoIcon from "@lucide/svelte/icons/info";
</script>
<div class="grid w-full max-w-sm gap-4">
<InputGroup.Root>
<InputGroup.Input id="email" placeholder="shadcn" />
<InputGroup.Addon>
<Label.Root for="email">@</Label.Root>
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root>
<InputGroup.Input id="email-2" placeholder="[email protected]" />
<InputGroup.Addon align="block-start">
<Label.Root for="email-2" class="text-foreground">Email</Label.Root>
<Tooltip.Root>
<Tooltip.Trigger>
{#snippet child({ props })}
<InputGroup.Button
{...props}
variant="ghost"
aria-label="Help"
class="ms-auto rounded-full"
size="icon-xs"
>
<InfoIcon />
</InputGroup.Button>
{/snippet}
</Tooltip.Trigger>
<Tooltip.Content>
<p>We'll use this to send you notifications</p>
</Tooltip.Content>
</Tooltip.Root>
</InputGroup.Addon>
</InputGroup.Root>
</div> Dropdown
Pair input groups with dropdown menus for complex interactions.
<script lang="ts">
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";
import * as InputGroup from "$lib/components/ui/input-group/index.js";
import ChevronDownIcon from "@lucide/svelte/icons/chevron-down";
import MoreHorizontalIcon from "@lucide/svelte/icons/more-horizontal";
</script>
<div class="grid w-full max-w-sm gap-4">
<InputGroup.Root>
<InputGroup.Input placeholder="Enter file name" />
<InputGroup.Addon align="inline-end">
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<InputGroup.Button
{...props}
variant="ghost"
aria-label="More"
size="icon-xs"
>
<MoreHorizontalIcon />
</InputGroup.Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content align="end">
<DropdownMenu.Item>Settings</DropdownMenu.Item>
<DropdownMenu.Item>Copy path</DropdownMenu.Item>
<DropdownMenu.Item>Open location</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
</InputGroup.Addon>
</InputGroup.Root>
<InputGroup.Root class="[--radius:1rem]">
<InputGroup.Input placeholder="Enter search query" />
<InputGroup.Addon align="inline-end">
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<InputGroup.Button
{...props}
variant="ghost"
class="!pe-1.5 text-xs"
>
Search In... <ChevronDownIcon class="size-3" />
</InputGroup.Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content align="end" class="[--radius:0.95rem]">
<DropdownMenu.Item>Documentation</DropdownMenu.Item>
<DropdownMenu.Item>Blog Posts</DropdownMenu.Item>
<DropdownMenu.Item>Changelog</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
</InputGroup.Addon>
</InputGroup.Root>
</div> Button Group
Wrap input groups with button groups to create prefixes and suffixes.
<script lang="ts">
import * as ButtonGroup from "$lib/components/ui/button-group/index.js";
import * as InputGroup from "$lib/components/ui/input-group/index.js";
import * as Label from "$lib/components/ui/label/index.js";
import Link2Icon from "@lucide/svelte/icons/link-2";
</script>
<div class="grid w-full max-w-sm gap-6">
<ButtonGroup.Root>
<ButtonGroup.Text>
<Label.Root for="url">https://</Label.Root>
</ButtonGroup.Text>
<InputGroup.Root>
<InputGroup.Input id="url" />
<InputGroup.Addon align="inline-end">
<Link2Icon />
</InputGroup.Addon>
</InputGroup.Root>
<ButtonGroup.Text>.com</ButtonGroup.Text>
</ButtonGroup.Root>
</div> Custom Input
Add the data-slot="input-group-control" attribute to your custom input for automatic behavior and focus state handling.
No style is applied to the custom input. Apply your own styles using the class prop.
<script lang="ts">
import * as InputGroup from "$lib/components/ui/input-group/index.js";
</script>
<div class="grid w-full max-w-sm gap-6">
<InputGroup.Root>
<textarea
data-slot="input-group-control"
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"
placeholder="Autoresize textarea..."></textarea>
<InputGroup.Addon align="block-end">
<InputGroup.Button class="ms-auto" size="sm" variant="default"
>Submit</InputGroup.Button
>
</InputGroup.Addon>
</InputGroup.Root>
</div>