Meet Frontfriend.Your brand. Your rules. Your agents, connected.Explore what’s new
DocumentationInputgroup

Inputgroup

Interactive preview

Rendered from the shared Frontfriend stories with React Base, the same preview Studio shows. Styling comes from your Design System; the code below is native to React Aria.

Installation

Complete project setup first. Your configuration must select react/aria and include your Design System ID.

pnpm exec frontfriend download input-group -f react --primitive aria

Review dependencies reported by the downloader and keep your source changes in version control.

Usage

import {
  InputGroup,
  InputGroupInput,
  InputGroupAddon,
} from "@/components/ui/input-group";
<InputGroup className="w-80">
  <InputGroupInput
    placeholder="Search..."
    disabled={false}
  />
  <InputGroupAddon align="inline-start">
    Search
  </InputGroupAddon>
</InputGroup>;

Examples

Demo

import {
  InputGroup,
  InputGroupInput,
  InputGroupAddon,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
<InputGroup className="w-[320px] max-w-xs">
  <InputGroupInput placeholder="Search..." />
  <InputGroupAddon>
    <DsIcon name="Search" size="4" />
  </InputGroupAddon>
  <InputGroupAddon align="inline-end">12 results</InputGroupAddon>
</InputGroup>;

Inline Start

import { Field, FieldLabel, FieldDescription } from "@/components/ui/field";
import {
  InputGroup,
  InputGroupInput,
  InputGroupAddon,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
<Field className="w-[360px] max-w-sm">
  <FieldLabel htmlFor="inline-start-input">Input</FieldLabel>
  <InputGroup>
    <InputGroupInput id="inline-start-input" placeholder="Search..." />
    <InputGroupAddon align="inline-start">
      <DsIcon name="Search" size="4" />
    </InputGroupAddon>
  </InputGroup>
  <FieldDescription>Icon positioned at the start.</FieldDescription>
</Field>;

Inline End

import { Field, FieldLabel, FieldDescription } from "@/components/ui/field";
import {
  InputGroup,
  InputGroupInput,
  InputGroupAddon,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
<Field className="w-[360px] max-w-sm">
  <FieldLabel htmlFor="inline-end-input">Input</FieldLabel>
  <InputGroup>
    <InputGroupInput
      id="inline-end-input"
      type="password"
      placeholder="Enter password"
    />
    <InputGroupAddon align="inline-end">
      <DsIcon name="EyeOff" size="4" />
    </InputGroupAddon>
  </InputGroup>
  <FieldDescription>Icon positioned at the end.</FieldDescription>
</Field>;

Block Start

import {
  FieldGroup,
  Field,
  FieldLabel,
  FieldDescription,
} from "@/components/ui/field";
import {
  InputGroup,
  InputGroupInput,
  InputGroupAddon,
  InputGroupText,
  InputGroupTextarea,
  InputGroupButton,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
<FieldGroup className="w-[360px] max-w-sm">
  <Field className="w-full">
    <FieldLabel htmlFor="block-start-input">Input</FieldLabel>
    <InputGroup className="h-auto">
      <InputGroupInput id="block-start-input" placeholder="Enter your name" />
      <InputGroupAddon align="block-start">
        <InputGroupText>Full Name</InputGroupText>
      </InputGroupAddon>
    </InputGroup>
    <FieldDescription>Header positioned above the input.</FieldDescription>
  </Field>
  <Field className="w-full">
    <FieldLabel htmlFor="block-start-textarea">Textarea</FieldLabel>
    <InputGroup>
      <InputGroupTextarea
        id="block-start-textarea"
        placeholder="console.log('Hello, world!');"
        className="font-mono text-sm"
      />
      <InputGroupAddon align="block-start" className="border-b">
        <DsIcon name="FileCode" size="4" />
        <InputGroupText className="font-mono">script.js</InputGroupText>
        <InputGroupButton size="icon-xs" className="ml-auto">
          <DsIcon name="Copy" size="4" />
          <span className="sr-only">Copy</span>
        </InputGroupButton>
      </InputGroupAddon>
    </InputGroup>
    <FieldDescription>Header positioned above the textarea.</FieldDescription>
  </Field>
</FieldGroup>;

Block End

import {
  FieldGroup,
  Field,
  FieldLabel,
  FieldDescription,
} from "@/components/ui/field";
import {
  InputGroup,
  InputGroupInput,
  InputGroupAddon,
  InputGroupText,
  InputGroupTextarea,
  InputGroupButton,
} from "@/components/ui/input-group";
<FieldGroup className="w-[360px] max-w-sm">
  <Field className="w-full">
    <FieldLabel htmlFor="block-end-input">Input</FieldLabel>
    <InputGroup className="h-auto">
      <InputGroupInput id="block-end-input" placeholder="Enter amount" />
      <InputGroupAddon align="block-end">
        <InputGroupText>USD</InputGroupText>
      </InputGroupAddon>
    </InputGroup>
    <FieldDescription>Footer positioned below the input.</FieldDescription>
  </Field>
  <Field className="w-full">
    <FieldLabel htmlFor="block-end-textarea">Textarea</FieldLabel>
    <InputGroup>
      <InputGroupTextarea
        id="block-end-textarea"
        placeholder="Write a comment..."
      />
      <InputGroupAddon align="block-end" className="border-t">
        <InputGroupText>0/280</InputGroupText>
        <InputGroupButton variant="default" size="sm" className="ml-auto">
          Post
        </InputGroupButton>
      </InputGroupAddon>
    </InputGroup>
    <FieldDescription>Footer positioned below the textarea.</FieldDescription>
  </Field>
</FieldGroup>;

Icon

import {
  InputGroup,
  InputGroupInput,
  InputGroupAddon,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
<div className="grid w-[360px] max-w-sm gap-6">
  <InputGroup>
    <InputGroupInput placeholder="Search..." />
    <InputGroupAddon>
      <DsIcon name="Search" size="4" />
    </InputGroupAddon>
  </InputGroup>
  <InputGroup>
    <InputGroupInput type="email" placeholder="Enter your email" />
    <InputGroupAddon>
      <DsIcon name="Mail" size="4" />
    </InputGroupAddon>
  </InputGroup>
  <InputGroup>
    <InputGroupInput placeholder="Card number" />
    <InputGroupAddon>
      <DsIcon name="CreditCard" size="4" />
    </InputGroupAddon>
    <InputGroupAddon align="inline-end">
      <DsIcon name="Check" size="4" />
    </InputGroupAddon>
  </InputGroup>
  <InputGroup>
    <InputGroupInput placeholder="Card number" />
    <InputGroupAddon align="inline-end">
      <DsIcon name="Star" size="4" />
      <DsIcon name="Search" size="4" />
    </InputGroupAddon>
  </InputGroup>
</div>;

Text

import {
  InputGroup,
  InputGroupAddon,
  InputGroupText,
  InputGroupInput,
  InputGroupTextarea,
} from "@/components/ui/input-group";
<div className="grid w-[360px] max-w-sm gap-6">
  <InputGroup>
    <InputGroupAddon>
      <InputGroupText>$</InputGroupText>
    </InputGroupAddon>
    <InputGroupInput placeholder="0.00" />
    <InputGroupAddon align="inline-end">
      <InputGroupText>USD</InputGroupText>
    </InputGroupAddon>
  </InputGroup>
  <InputGroup>
    <InputGroupAddon>
      <InputGroupText>https://</InputGroupText>
    </InputGroupAddon>
    <InputGroupInput placeholder="example.com" />
    <InputGroupAddon align="inline-end">
      <InputGroupText>.com</InputGroupText>
    </InputGroupAddon>
  </InputGroup>
  <InputGroup>
    <InputGroupInput placeholder="Enter your username" />
    <InputGroupAddon align="inline-end">
      <InputGroupText>@company.com</InputGroupText>
    </InputGroupAddon>
  </InputGroup>
  <InputGroup>
    <InputGroupTextarea placeholder="Enter your message" />
    <InputGroupAddon align="block-end">
      <InputGroupText className="text-xs">120 characters left</InputGroupText>
    </InputGroupAddon>
  </InputGroup>
</div>;

Button

import {
  InputGroup,
  InputGroupInput,
  InputGroupAddon,
  InputGroupButton,
  InputGroupText,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
<div className="grid w-[360px] max-w-sm gap-6">
  <InputGroup>
    <InputGroupInput placeholder="https://x.com/shadcn" readOnly />
    <InputGroupAddon align="inline-end">
      <InputGroupButton aria-label="Copy" title="Copy" size="icon-xs">
        <DsIcon name="Copy" size="4" />
      </InputGroupButton>
    </InputGroupAddon>
  </InputGroup>
  <InputGroup>
    <InputGroupAddon>
      <InputGroupText>https://</InputGroupText>
    </InputGroupAddon>
    <InputGroupInput placeholder="frontfriend.dev" />
    <InputGroupAddon align="inline-end">
      <InputGroupButton size="icon-xs">
        <DsIcon name="Star" size="4" />
      </InputGroupButton>
    </InputGroupAddon>
  </InputGroup>
  <InputGroup>
    <InputGroupInput placeholder="Type to search..." />
    <InputGroupAddon align="inline-end">
      <InputGroupButton variant="secondary">Search</InputGroupButton>
    </InputGroupAddon>
  </InputGroup>
</div>;

Kbd Example

import {
  InputGroup,
  InputGroupInput,
  InputGroupAddon,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
import { Kbd } from "@/components/ui/kbd";
<InputGroup className="w-[360px] max-w-sm">
  <InputGroupInput placeholder="Search..." />
  <InputGroupAddon>
    <DsIcon name="Search" size="4" />
  </InputGroupAddon>
  <InputGroupAddon align="inline-end">
    <Kbd>⌘K</Kbd>
  </InputGroupAddon>
</InputGroup>;

Dropdown

import {
  InputGroup,
  InputGroupInput,
  InputGroupAddon,
  InputGroupButton,
} from "@/components/ui/input-group";
import {
  DropdownMenuTrigger,
  DropdownMenu,
  DropdownMenuGroup,
  DropdownMenuItem,
} from "@/components/ui/dropdown-menu";
import { Icon as DsIcon } from "@/components/ui/icon";
<div className="grid w-[360px] max-w-sm gap-4">
  <InputGroup>
    <InputGroupInput placeholder="Enter file name" />
    <InputGroupAddon align="inline-end">
      <DropdownMenuTrigger>
        <InputGroupButton variant="ghost" aria-label="More" size="icon-xs">
          <DsIcon name="Ellipsis" size="4" />
        </InputGroupButton>
        <DropdownMenu align="end">
          <DropdownMenuGroup>
            <DropdownMenuItem>Settings</DropdownMenuItem>
            <DropdownMenuItem>Copy path</DropdownMenuItem>
            <DropdownMenuItem>Open location</DropdownMenuItem>
          </DropdownMenuGroup>
        </DropdownMenu>
      </DropdownMenuTrigger>
    </InputGroupAddon>
  </InputGroup>
  <InputGroup>
    <InputGroupInput placeholder="Enter search query" />
    <InputGroupAddon align="inline-end">
      <InputGroupButton className="pr-1.5 text-xs">
        Search In... <DsIcon name="ChevronDown" size="3" />
      </InputGroupButton>
    </InputGroupAddon>
  </InputGroup>
</div>;

Spinner Example

import {
  InputGroup,
  InputGroupInput,
  InputGroupAddon,
  InputGroupText,
} from "@/components/ui/input-group";
import { Spinner } from "@/components/ui/spinner";
import { Icon as DsIcon } from "@/components/ui/icon";
<div className="grid w-[360px] max-w-sm gap-4">
  <InputGroup>
    <InputGroupInput placeholder="Searching..." />
    <InputGroupAddon align="inline-end">
      <Spinner />
    </InputGroupAddon>
  </InputGroup>
  <InputGroup>
    <InputGroupInput placeholder="Processing..." />
    <InputGroupAddon>
      <Spinner />
    </InputGroupAddon>
  </InputGroup>
  <InputGroup>
    <InputGroupInput placeholder="Saving changes..." />
    <InputGroupAddon align="inline-end">
      <InputGroupText>Saving...</InputGroupText>
      <Spinner />
    </InputGroupAddon>
  </InputGroup>
  <InputGroup>
    <InputGroupInput placeholder="Refreshing data..." />
    <InputGroupAddon>
      <DsIcon name="Loader" size="4" className="animate-spin" />
    </InputGroupAddon>
    <InputGroupAddon align="inline-end">
      <InputGroupText>Please wait...</InputGroupText>
    </InputGroupAddon>
  </InputGroup>
</div>;

Textarea

import {
  InputGroup,
  InputGroupTextarea,
  InputGroupAddon,
  InputGroupText,
  InputGroupButton,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
<div className="grid w-[448px] max-w-md gap-4">
  <InputGroup>
    <InputGroupTextarea
      placeholder="console.log('Hello, world!');"
      className="min-h-[200px]"
    />
    <InputGroupAddon align="block-end" className="border-t">
      <InputGroupText>Line 1, Column 1</InputGroupText>
      <InputGroupButton size="sm" className="ml-auto" variant="default">
        Run
      </InputGroupButton>
    </InputGroupAddon>
    <InputGroupAddon align="block-start" className="border-b">
      <InputGroupText className="font-mono font-medium">
        <DsIcon name="FileCode" size="4" /> script.js
      </InputGroupText>
      <InputGroupButton className="ml-auto" size="icon-xs">
        <DsIcon name="RefreshCw" size="4" />
      </InputGroupButton>
      <InputGroupButton variant="ghost" size="icon-xs">
        <DsIcon name="Copy" size="4" />
      </InputGroupButton>
    </InputGroupAddon>
  </InputGroup>
</div>;

Custom Input

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
} from "@/components/ui/input-group";
<div className="grid w-[360px] max-w-sm gap-6">
  <InputGroup>
    <textarea
      data-slot="input-group-control"
      className="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..."
    />
    <InputGroupAddon align="block-end">
      <InputGroupButton className="ml-auto" size="sm" variant="default">
        Submit
      </InputGroupButton>
    </InputGroupAddon>
  </InputGroup>
</div>;

API reference

Frontfriend-owned props. Native HTML and inherited primitive props remain documented by React Aria.

InputGroupAddon

Prop Type Default Description
align InputGroupAlign "inline-start"

InputGroupButton

Prop Type Default Description
size InputGroupButtonSize "xs"

Configuration determines the actual classes and available styling hooks. See Theming & component configuration before introducing a repeated local override.