Skip to content
SevenUI
Esc
navigateopen⌘Jpreview
Sign in
On this page

Input Group

Groups an input with addons, buttons, and inline text.

"use client";

import { SearchIcon } from "lucide-react";

import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
  InputGroupText,
} from "@/registry/base/ui/input-group";
import { Kbd } from "@/registry/base/ui/kbd";

export default function InputGroupDemo() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-4">
      <InputGroup>
        <InputGroupInput placeholder="Search components..." />
        <InputGroupAddon>
          <SearchIcon />
        </InputGroupAddon>
        <InputGroupAddon align="inline-end">
          <Kbd>⌘K</Kbd>
        </InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupInput placeholder="yourname" />
        <InputGroupAddon align="inline-end">
          <InputGroupText>@sevenui.dev</InputGroupText>
        </InputGroupAddon>
      </InputGroup>
    </div>
  );
}

Installation

npx shadcn@latest add https://sevenui.dev/r/input-group.json

Usage

import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
  InputGroupText,
} from "@/components/ui/input-group";
import { SearchIcon } from "lucide-react";

export function Search() {
  return (
    <InputGroup>
      <InputGroupInput placeholder="Search..." />
      <InputGroupAddon>
        <SearchIcon />
      </InputGroupAddon>
    </InputGroup>
  );
}

Place addons after the input in the DOM and control their visual position with the align prop. Use inline-start / inline-end for single-line inputs and block-start / block-end for textareas. Clicking an addon focuses the grouped input.

Examples

With button

Buttons inside an addon use InputGroupButton, which renders the SevenUI Button with compact sizing (xs by default, ghost variant).

"use client";

import * as React from "react";
import { CheckIcon, CopyIcon } from "lucide-react";

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
} from "@/registry/base/ui/input-group";

const INSTALL_COMMAND =
  "npx shadcn@latest add https://sevenui.dev/r/input-group.json";

export default function InputGroupButtonDemo() {
  const [copied, setCopied] = React.useState(false);

  return (
    <div className="flex w-full max-w-sm flex-col gap-4">
      <InputGroup>
        <InputGroupInput readOnly value={INSTALL_COMMAND} />
        <InputGroupAddon align="inline-end">
          <InputGroupButton
            size="icon-xs"
            aria-label="Copy command"
            onClick={() => {
              navigator.clipboard.writeText(INSTALL_COMMAND);
              setCopied(true);
              setTimeout(() => setCopied(false), 1500);
            }}
          >
            {copied ? <CheckIcon /> : <CopyIcon />}
          </InputGroupButton>
        </InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupInput type="password" placeholder="Enter your API key" />
        <InputGroupAddon align="inline-end">
          <InputGroupButton variant="secondary">Verify</InputGroupButton>
        </InputGroupAddon>
      </InputGroup>
    </div>
  );
}

Textarea

InputGroupTextarea pairs with a block-end addon for prompt-style composers. block-start works the same way for headers.

"use client";

import { ArrowUpIcon, PaperclipIcon } from "lucide-react";

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupText,
  InputGroupTextarea,
} from "@/registry/base/ui/input-group";

export default function InputGroupTextareaDemo() {
  return (
    <div className="w-full max-w-md">
      <InputGroup>
        <InputGroupAddon align="block-start" className="border-b">
          <InputGroupText>New message</InputGroupText>
        </InputGroupAddon>
        <InputGroupTextarea placeholder="Ask anything..." rows={3} />
        <InputGroupAddon align="block-end">
          <InputGroupButton size="icon-xs" aria-label="Attach file">
            <PaperclipIcon />
          </InputGroupButton>
          <InputGroupText className="ml-auto">12,000 tokens</InputGroupText>
          <InputGroupButton
            size="icon-xs"
            variant="default"
            aria-label="Send message"
          >
            <ArrowUpIcon />
          </InputGroupButton>
        </InputGroupAddon>
      </InputGroup>
    </div>
  );
}

API reference

InputGroup

Extends div with role="group". Wraps one control (InputGroupInput or InputGroupTextarea) plus any number of addons. Focus, disabled, and invalid styles react to the grouped control’s state.

InputGroupAddon

Extends div. Clicking it focuses the grouped input (clicks on buttons inside are ignored).

Prop Type Default
align "inline-start" | "inline-end" | "block-start" | "block-end" "inline-start"

InputGroupButton

Extends Button — all Button props apply except size, which is replaced by compact group sizes. type defaults to "button".

Prop Type Default
variant Button variants "ghost"
size "xs" | "sm" | "icon-xs" | "icon-sm" "xs"

InputGroupInput

Extends Input — all Input props apply. Renders borderless inside the group, which draws the border and focus ring.

InputGroupTextarea

Extends Textarea — all Textarea props apply. Renders borderless inside the group; the group grows to fit.

InputGroupText

Extends span. Muted inline text for prefixes, suffixes, and counters.

Was this page helpful?