Input Group

Compose text inputs and textareas with inline add-ons, icons, and buttons inside a single bordered control.

<InputGroup className="max-w-xs">  <InputGroupInput placeholder="Search…" type="search" />  <InputGroupAddon align="inline-end">    <MagnifyingGlass className="text-fg-muted" />  </InputGroupAddon></InputGroup>

Usage

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
  InputGroupText,
  InputGroupTextarea,
} from "@reva/ui";

Use InputGroupInput or InputGroupTextarea inside InputGroup — not the standalone Input / raw <input>, so focus rings and borders stay on the outer shell.

InputGroupButton is a thin wrapper around buttonVariants (same variant, size, and layout as Button). Default is variant="ghost", size="sm", layout="square" for compact trailing actions.

Don't set className="size-4" (or any other size-*) on icon glyphs inside InputGroupButton or InputGroupAddon — both auto-size their direct <svg> children to the correct step (16px / 20px / 24px depending on button size). Just write <Eye /> and the right glyph size follows from the button or addon scale.

Composition

Use the following composition to build an InputGroup:

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

Place InputGroupAddon after the control in the DOM and set align (inline-start | inline-end | block-start | block-end) to position it visually.

Examples

Sizes

Set size on InputGroup so the shell height matches Input (sm / default / lg / xl). InputGroupInput and InputGroupTextarea inherit the group size; pass size on the control to override. The same applies to inline actions — addons sized via the group context keep the same optical inset on top, right, and bottom across every size.

<VStack className="w-full max-w-sm" gap={10}>  <VStack className="w-full" gap={3}>    {/* Icon-only addon — sized via InputGroup context */}    <InputGroup size="sm">      <InputGroupInput placeholder="Small group" type="search" />      <InputGroupAddon align="inline-end">        <MagnifyingGlass className="text-fg-muted" />      </InputGroupAddon>    </InputGroup>    <InputGroup size="default">      <InputGroupInput placeholder="Default group" type="search" />      <InputGroupAddon align="inline-end">        <MagnifyingGlass className="text-fg-muted" />      </InputGroupAddon>    </InputGroup>    <InputGroup size="lg">      <InputGroupInput placeholder="Large group (lg)" type="search" />      <InputGroupAddon align="inline-end">        <MagnifyingGlass className="text-fg-muted" />      </InputGroupAddon>    </InputGroup>    <InputGroup size="xl">      <InputGroupInput placeholder="Extra large group (xl)" type="search" />      <InputGroupAddon align="inline-end">        <MagnifyingGlass className="text-fg-muted" />      </InputGroupAddon>    </InputGroup>  </VStack>  <VStack className="w-full" gap={3}>    {/* Inline action — ghost InputGroupButton (see Password example for state wiring) */}    <InputGroup size="sm">      <InputGroupInput placeholder="Enter password" type="password" />      <InputGroupAddon align="inline-end">        <InputGroupButton aria-label="Show password" type="button">          <Eye />        </InputGroupButton>      </InputGroupAddon>    </InputGroup>    <InputGroup size="default">      <InputGroupInput placeholder="Enter password" type="password" />      <InputGroupAddon align="inline-end">        <InputGroupButton aria-label="Show password" type="button">          <Eye />        </InputGroupButton>      </InputGroupAddon>    </InputGroup>    <InputGroup size="lg">      <InputGroupInput placeholder="Enter password" type="password" />      <InputGroupAddon align="inline-end">        <InputGroupButton aria-label="Show password" type="button">          <Eye />        </InputGroupButton>      </InputGroupAddon>    </InputGroup>    <InputGroup size="xl">      <InputGroupInput placeholder="Enter password" type="password" />      <InputGroupAddon align="inline-end">        <InputGroupButton aria-label="Show password" type="button">          <Eye />        </InputGroupButton>      </InputGroupAddon>    </InputGroup>  </VStack></VStack>

Password with show / hide

Trailing icon button toggles type between password and text. Implemented as InputGroupPasswordDemo in the docs app (useState + "use client").

"use client";import {InputGroup,InputGroupAddon,InputGroupButton,InputGroupInput,} from "@reva/ui";import { Eye, EyeSlash } from "@phosphor-icons/react/ssr";import { useState } from "react";export function PasswordField() {const [showPassword, setShowPassword] = useState(false);return (  <InputGroup className="max-w-sm">    <InputGroupInput      autoComplete="current-password"      id="password"      name="password"      placeholder="Enter password"      type={showPassword ? "text" : "password"}    />    <InputGroupAddon align="inline-end">      <InputGroupButton        aria-label={showPassword ? "Hide password" : "Show password"}        aria-pressed={showPassword}        type="button"        onClick={() => setShowPassword((v) => !v)}      >        {showPassword ? <EyeSlash /> : <Eye />}      </InputGroupButton>    </InputGroupAddon>  </InputGroup>);}

Search with trailing button

import {InputGroup,InputGroupAddon,InputGroupButton,InputGroupInput,} from "@reva/ui";import { MagnifyingGlass } from "@phosphor-icons/react/ssr";<InputGroup className="max-w-sm"><InputGroupInput placeholder="Type to search…" type="search" /><InputGroupAddon align="inline-end">  <InputGroupButton layout="text" size="sm" type="button" variant="secondary">    <MagnifyingGlass />    Search  </InputGroupButton></InputGroupAddon></InputGroup>

Props

PartNotes
InputGroupReact.ComponentProps<"div"> — single bordered shell. Optional size: sm | default | lg | xl (heights 32px / 40px / 44px / 48px; default default).
InputGroupInputForwards to Input with borderless styles inside the group. Optional size overrides the group’s scale when set.
InputGroupTextareaForwards to Textarea with borderless styles inside the group. Optional size overrides the group’s scale when set.
InputGroupAddonalign: inline-start (default), inline-end, block-start, block-end.
InputGroupButtonNative <button> + buttonVariantsvariant, size (xs | sm | default | lg), layout (text | square).
InputGroupTextInline label / hint text inside an addon.

On this page