Skip to content

Input Group

Display additional information or actions to an input or textarea.

12 results
https://
52% used
<script lang="ts">
  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 ArrowUpIcon from "phosphor-svelte/lib/ArrowUpIcon";
  import CheckIcon from "phosphor-svelte/lib/CheckIcon";
  import InfoIcon from "phosphor-svelte/lib/InfoIcon";
  import MagnifyingGlassIcon from "phosphor-svelte/lib/MagnifyingGlassIcon";
  import PlusIcon from "phosphor-svelte/lib/PlusIcon";
</script>
 
<div class="grid w-full max-w-sm gap-6">
  <InputGroup.Root>
    <InputGroup.Input placeholder="Search..." />
    <InputGroup.Addon>
      <MagnifyingGlassIcon />
    </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">
              <InfoIcon />
            </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">
        <PlusIcon />
      </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="@kura" />
    <InputGroup.Addon align="inline-end">
      <div
        class="bg-primary text-primary-foreground flex size-4 items-center justify-center rounded-full"
      >
        <CheckIcon class="size-3" />
      </div>
    </InputGroup.Addon>
  </InputGroup.Root>
</div>

Installation

pnpm dlx jsrepo@latest add input-group

Usage

<script lang="ts">
  import * as InputGroup from '$lib/components/ui/input-group/index.js';
  import MagnifyingGlassIcon from 'phosphor-svelte/lib/MagnifyingGlassIcon';
</script>
<InputGroup.Root>
  <InputGroup.Input placeholder="Search..." />
  <InputGroup.Addon>
    <MagnifyingGlassIcon />
  </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 "phosphor-svelte/lib/CheckIcon";
  import CreditCardIcon from "phosphor-svelte/lib/CreditCardIcon";
  import EnvelopeSimpleIcon from "phosphor-svelte/lib/EnvelopeSimpleIcon";
  import InfoIcon from "phosphor-svelte/lib/InfoIcon";
  import MagnifyingGlassIcon from "phosphor-svelte/lib/MagnifyingGlassIcon";
  import StarIcon from "phosphor-svelte/lib/StarIcon";
</script>
 
<div class="grid w-full max-w-sm gap-6">
  <InputGroup.Root>
    <InputGroup.Input placeholder="Search..." />
    <InputGroup.Addon>
      <MagnifyingGlassIcon />
    </InputGroup.Addon>
  </InputGroup.Root>
  <InputGroup.Root>
    <InputGroup.Input type="email" placeholder="Enter your email" />
    <InputGroup.Addon>
      <EnvelopeSimpleIcon />
    </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.

$
USD
https://
.com
@company.com
120 characters left
<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.

https://
<script lang="ts">
  import CheckIcon from "phosphor-svelte/lib/CheckIcon";
  import CopyIcon from "phosphor-svelte/lib/CopyIcon";
  import InfoIcon from "phosphor-svelte/lib/InfoIcon";
  import StarIcon from "phosphor-svelte/lib/StarIcon";
 
  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://kura.gfdc.dev" readonly />
    <InputGroup.Addon align="inline-end">
      <InputGroup.Button
        aria-label="Copy"
        title="Copy"
        size="icon-xs"
        onclick={() => clipboard.copy("https://kura.gfdc.dev")}
      >
        {#if clipboard.copied}
          <CheckIcon />
        {:else}
          <CopyIcon />
        {/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">
              <InfoIcon />
            </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"
      >
        <StarIcon 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 InfoIcon from "phosphor-svelte/lib/InfoIcon";
  import QuestionIcon from "phosphor-svelte/lib/QuestionIcon";
</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"
            >
              <QuestionIcon />
            </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"
            >
              <QuestionIcon />
            </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.js
Line 1, Column 1
<script lang="ts">
  import ArrowBendDownLeftIcon from "phosphor-svelte/lib/ArrowBendDownLeftIcon";
  import ArrowClockwiseIcon from "phosphor-svelte/lib/ArrowClockwiseIcon";
  import CopyIcon from "phosphor-svelte/lib/CopyIcon";
  import FileJsIcon from "phosphor-svelte/lib/FileJsIcon";
 
  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">
        <FileJsIcon />
        script.js
      </InputGroup.Text>
      <InputGroup.Button class="ms-auto" size="icon-xs">
        <ArrowClockwiseIcon />
      </InputGroup.Button>
      <InputGroup.Button variant="ghost" size="icon-xs">
        <CopyIcon />
      </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 <ArrowBendDownLeftIcon />
      </InputGroup.Button>
    </InputGroup.Addon>
  </InputGroup.Root>
</div>

Spinner

Show loading indicators while processing input.

Saving...
Please wait...
<script lang="ts">
  import * as InputGroup from "$lib/components/ui/input-group/index.js";
  import { Spinner } from "$lib/components/ui/spinner/index.js";
  import SpinnerGapIcon from "phosphor-svelte/lib/SpinnerGapIcon";
</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>
      <SpinnerGapIcon 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 "phosphor-svelte/lib/InfoIcon";
</script>
 
<div class="grid w-full max-w-sm gap-4">
  <InputGroup.Root>
    <InputGroup.Input id="email" placeholder="kura" />
    <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>

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 CaretDownIcon from "phosphor-svelte/lib/CaretDownIcon";
  import DotsThreeIcon from "phosphor-svelte/lib/DotsThreeIcon";
</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"
            >
              <DotsThreeIcon />
            </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... <CaretDownIcon 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.

.com
<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 LinkIcon from "phosphor-svelte/lib/LinkIcon";
</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">
        <LinkIcon />
      </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>