Command Palette

Search for a command...

Input Group

Display additional information or actions to an input or textarea.

12 results
https://
52% used
import { LuArrowUp, LuSearch } from "react-icons/lu";
import { TbCheck, TbInfoCircle, TbPlus } from "react-icons/tb";
import { css } from "styled-system/css";
import { styled } from "styled-system/jsx";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
  InputGroupText,
  InputGroupTextarea,
} from "@/components/ui/input-group";
import { Separator } from "@/components/ui/separator";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";

export default function InputGroupDemo() {
  return (
    <styled.div css={{ display: "grid", w: "full", maxW: "sm", gap: "6" }}>
      <InputGroup>
        <InputGroupInput placeholder="Search..." />
        <InputGroupAddon>
          <LuSearch />
        </InputGroupAddon>
        <InputGroupAddon align="inline-end">12 results</InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupInput placeholder="example.com" css={{ pl: "1!" }} />
        <InputGroupAddon>
          <InputGroupText>https://</InputGroupText>
        </InputGroupAddon>
        <InputGroupAddon align="inline-end">
          <Tooltip>
            <TooltipTrigger asChild>
              <InputGroupButton css={{ rounded: "full" }} size="icon-xs">
                <TbInfoCircle />
              </InputGroupButton>
            </TooltipTrigger>
            <TooltipContent>This is content in a tooltip.</TooltipContent>
          </Tooltip>
        </InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupTextarea placeholder="Ask, Search or Chat..." />
        <InputGroupAddon align="block-end">
          <InputGroupButton variant="outline" css={{ rounded: "full" }} size="icon-xs">
            <TbPlus />
          </InputGroupButton>
          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <InputGroupButton variant="ghost">Auto</InputGroupButton>
            </DropdownMenuTrigger>
            <DropdownMenuContent side="top" align="start">
              <DropdownMenuItem>Auto</DropdownMenuItem>
              <DropdownMenuItem>Agent</DropdownMenuItem>
              <DropdownMenuItem>Manual</DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
          <InputGroupText css={{ ml: "auto" }}>52% used</InputGroupText>
          <Separator orientation="vertical" css={{ h: "4!" }} />
          <InputGroupButton css={{ rounded: "full" }} size="icon-xs" disabled>
            <LuArrowUp />
            <styled.span css={{ srOnly: true }}>Send</styled.span>
          </InputGroupButton>
        </InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupInput placeholder="@shadcn" />
        <InputGroupAddon align="inline-end">
          <styled.div
            css={{
              bg: "primary",
              color: "primary.fg",
              display: "flex",
              w: "4",
              h: "4",
              alignItems: "center",
              justifyContent: "center",
              rounded: "full",
            }}
          >
            <TbCheck className={css({ w: "3", h: "3" })} />
          </styled.div>
        </InputGroupAddon>
      </InputGroup>
    </styled.div>
  );
}

Installation

npx nore-ui-cli@latest add input-group

Usage

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
  InputGroupText,
  InputGroupTextarea,
} from "@/components/ui/input-group";
<InputGroup>
  <InputGroupInput placeholder="Search..." />
  <InputGroupAddon>
    <SearchIcon />
  </InputGroupAddon>
  <InputGroupAddon align="inline-end">
    <InputGroupButton>Search</InputGroupButton>
  </InputGroupAddon>
</InputGroup>

Examples

Icon

import { LuCheck, LuCreditCard, LuInfo, LuMail, LuSearch, LuStar } from "react-icons/lu";
import { styled } from "styled-system/jsx";
import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";

export default function InputGroupIcon() {
  return (
    <styled.div css={{ display: "grid", w: "full", maxW: "sm", gap: "6" }}>
      <InputGroup>
        <InputGroupInput placeholder="Search..." />
        <InputGroupAddon>
          <LuSearch />
        </InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupInput type="email" placeholder="Enter your email" />
        <InputGroupAddon>
          <LuMail />
        </InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupInput placeholder="Card number" />
        <InputGroupAddon>
          <LuCreditCard />
        </InputGroupAddon>
        <InputGroupAddon align="inline-end">
          <LuCheck />
        </InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupInput placeholder="Card number" />
        <InputGroupAddon align="inline-end">
          <LuStar />
          <LuInfo />
        </InputGroupAddon>
      </InputGroup>
    </styled.div>
  );
}

Text

Display additional text information alongside inputs.

$
USD
https://
.com
@company.com
120 characters left
import { styled } from "styled-system/jsx";
import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
  InputGroupText,
  InputGroupTextarea,
} from "@/components/ui/input-group";

export default function InputGroupTextExample() {
  return (
    <styled.div css={{ display: "grid", w: "full", maxW: "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" css={{ pl: "0.5!" }} />
        <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 css={{ color: "muted.fg", textStyle: "xs" }}>
            120 characters left
          </InputGroupText>
        </InputGroupAddon>
      </InputGroup>
    </styled.div>
  );
}

Button

Add buttons to perform actions within the input group.

https://
"use client";

import * as React from "react";
import { TbCheck, TbCopy, TbInfoCircle, TbStar } from "react-icons/tb";
import { css } from "styled-system/css";
import { styled } from "styled-system/jsx";
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard";
import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
} from "@/components/ui/input-group";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";

export default function InputGroupButtonExample() {
  const { copyToClipboard, isCopied } = useCopyToClipboard();
  const [isFavorite, setIsFavorite] = React.useState(false);

  return (
    <styled.div css={{ display: "grid", w: "full", maxW: "sm", gap: "6" }}>
      <InputGroup>
        <InputGroupInput placeholder="https://x.com/shadcn" readOnly />
        <InputGroupAddon align="inline-end">
          <InputGroupButton
            aria-label="Copy"
            title="Copy"
            size="icon-xs"
            onClick={() => {
              copyToClipboard("https://x.com/shadcn");
            }}
          >
            {isCopied ? <TbCheck /> : <TbCopy />}
          </InputGroupButton>
        </InputGroupAddon>
      </InputGroup>
      <InputGroup css={{ rounded: "full" }}>
        <Popover>
          <PopoverTrigger asChild>
            <InputGroupAddon>
              <InputGroupButton variant="secondary" size="icon-xs" css={{ rounded: "full" }}>
                <TbInfoCircle />
              </InputGroupButton>
            </InputGroupAddon>
          </PopoverTrigger>
          <PopoverContent
            align="start"
            css={{ display: "flex", flexDir: "column", gap: "1", rounded: "xl", textStyle: "sm" }}
          >
            <styled.p css={{ fontWeight: "medium" }}>Your connection is not secure.</styled.p>
            <p>You should not enter any sensitive information on this site.</p>
          </PopoverContent>
        </Popover>
        <InputGroupAddon css={{ color: "muted.fg", pl: "1.5" }}>https://</InputGroupAddon>
        <InputGroupInput id="input-secure-19" />
        <InputGroupAddon align="inline-end">
          <InputGroupButton onClick={() => setIsFavorite(!isFavorite)} size="icon-xs">
            <TbStar
              data-favorite={isFavorite}
              className={css({
                "&[data-favorite=true]": {
                  fill: "blue.600",
                  stroke: "blue.600",
                },
              })}
            />
          </InputGroupButton>
        </InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupInput placeholder="Type to search..." />
        <InputGroupAddon align="inline-end">
          <InputGroupButton variant="secondary">Search</InputGroupButton>
        </InputGroupAddon>
      </InputGroup>
    </styled.div>
  );
}

Tooltip

Add tooltips to provide additional context or help.

import { LuCircleHelp, LuInfo } from "react-icons/lu";
import { styled } from "styled-system/jsx";
import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
} from "@/components/ui/input-group";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";

export default function InputGroupTooltip() {
  return (
    <styled.div css={{ display: "grid", w: "full", maxW: "sm", gap: "4" }}>
      <InputGroup>
        <InputGroupInput placeholder="Enter password" type="password" />
        <InputGroupAddon align="inline-end">
          <Tooltip>
            <TooltipTrigger asChild>
              <InputGroupButton variant="ghost" aria-label="Info" size="icon-xs">
                <LuInfo />
              </InputGroupButton>
            </TooltipTrigger>
            <TooltipContent>
              <p>Password must be at least 8 characters</p>
            </TooltipContent>
          </Tooltip>
        </InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupInput placeholder="Your email address" />
        <InputGroupAddon align="inline-end">
          <Tooltip>
            <TooltipTrigger asChild>
              <InputGroupButton variant="ghost" aria-label="Help" size="icon-xs">
                <LuCircleHelp />
              </InputGroupButton>
            </TooltipTrigger>
            <TooltipContent>
              <p>We&apos;ll use this to send you notifications</p>
            </TooltipContent>
          </Tooltip>
        </InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupInput placeholder="Enter API key" />
        <Tooltip>
          <TooltipTrigger asChild>
            <InputGroupAddon>
              <InputGroupButton variant="ghost" aria-label="Help" size="icon-xs">
                <LuCircleHelp />
              </InputGroupButton>
            </InputGroupAddon>
          </TooltipTrigger>
          <TooltipContent side="left">
            <p>Click for help with API keys</p>
          </TooltipContent>
        </Tooltip>
      </InputGroup>
    </styled.div>
  );
}

Textarea

Input groups also work with textarea components. Use block-start or block-end for alignment.

Line 1, Column 1
script.js
import { TbBrandJavascript, TbCopy, TbCornerDownLeft, TbRefresh } from "react-icons/tb";
import { styled } from "styled-system/jsx";
import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupText,
  InputGroupTextarea,
} from "@/components/ui/input-group";

export default function InputGroupTextareaExample() {
  return (
    <styled.div css={{ display: "grid", w: "full", maxW: "md", gap: "4" }}>
      <InputGroup>
        <InputGroupTextarea
          id="textarea-code-32"
          placeholder="console.log('Hello, world!');"
          css={{ minH: "200px" }}
        />
        <InputGroupAddon align="block-end" css={{ borderTopWidth: "1px" }}>
          <InputGroupText>Line 1, Column 1</InputGroupText>
          <InputGroupButton size="sm" css={{ ml: "auto" }}>
            Run <TbCornerDownLeft />
          </InputGroupButton>
        </InputGroupAddon>
        <InputGroupAddon align="block-start" css={{ borderBottomWidth: "1px" }}>
          <InputGroupText css={{ fontFamily: "mono", fontWeight: "medium" }}>
            <TbBrandJavascript />
            script.js
          </InputGroupText>
          <InputGroupButton css={{ ml: "auto" }} size="icon-xs">
            <TbRefresh />
          </InputGroupButton>
          <InputGroupButton variant="ghost" size="icon-xs">
            <TbCopy />
          </InputGroupButton>
        </InputGroupAddon>
      </InputGroup>
    </styled.div>
  );
}

Spinner

Show loading indicators while processing input.

Saving...
Please wait...
import { LuLoader } from "react-icons/lu";
import { css } from "styled-system/css";
import { styled } from "styled-system/jsx";
import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
  InputGroupText,
} from "@/components/ui/input-group";
import { Spinner } from "@/components/ui/spinner";

export default function InputGroupSpinner() {
  return (
    <styled.div css={{ display: "grid", w: "full", maxW: "sm", gap: "4" }}>
      <InputGroup data-disabled>
        <InputGroupInput placeholder="Searching..." disabled />
        <InputGroupAddon align="inline-end">
          <Spinner />
        </InputGroupAddon>
      </InputGroup>
      <InputGroup data-disabled>
        <InputGroupInput placeholder="Processing..." disabled />
        <InputGroupAddon>
          <Spinner />
        </InputGroupAddon>
      </InputGroup>
      <InputGroup data-disabled>
        <InputGroupInput placeholder="Saving changes..." disabled />
        <InputGroupAddon align="inline-end">
          <InputGroupText>Saving...</InputGroupText>
          <Spinner />
        </InputGroupAddon>
      </InputGroup>
      <InputGroup data-disabled>
        <InputGroupInput placeholder="Refreshing data..." disabled />
        <InputGroupAddon>
          <LuLoader className={css({ animation: "spin" })} />
        </InputGroupAddon>
        <InputGroupAddon align="inline-end">
          <InputGroupText css={{ color: "muted.fg" }}>Please wait...</InputGroupText>
        </InputGroupAddon>
      </InputGroup>
    </styled.div>
  );
}

Label

Add labels within input groups to improve accessibility.

import { LuInfo } from "react-icons/lu";
import { styled } from "styled-system/jsx";
import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
} from "@/components/ui/input-group";
import { Label } from "@/components/ui/label";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";

export default function InputGroupLabel() {
  return (
    <styled.div css={{ display: "grid", w: "full", maxW: "sm", gap: "4" }}>
      <InputGroup>
        <InputGroupInput id="email" placeholder="shadcn" />
        <InputGroupAddon>
          <Label htmlFor="email">@</Label>
        </InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupInput id="email-2" placeholder="[email protected]" />
        <InputGroupAddon align="block-start">
          <Label htmlFor="email-2" css={{ color: "fg" }}>
            Email
          </Label>
          <Tooltip>
            <TooltipTrigger asChild>
              <InputGroupButton
                variant="ghost"
                aria-label="Help"
                css={{ ml: "auto", rounded: "full" }}
                size="icon-xs"
              >
                <LuInfo />
              </InputGroupButton>
            </TooltipTrigger>
            <TooltipContent>
              <p>We&apos;ll use this to send you notifications</p>
            </TooltipContent>
          </Tooltip>
        </InputGroupAddon>
      </InputGroup>
    </styled.div>
  );
}

Pair input groups with dropdown menus for complex interactions.

import { LuChevronDown, LuEllipsis } from "react-icons/lu";
import { css } from "styled-system/css";
import { styled } from "styled-system/jsx";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
} from "@/components/ui/input-group";

export default function InputGroupDropdown() {
  return (
    <styled.div css={{ display: "grid", w: "full", maxW: "sm", gap: "4" }}>
      <InputGroup>
        <InputGroupInput placeholder="Enter file name" />
        <InputGroupAddon align="inline-end">
          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <InputGroupButton variant="ghost" aria-label="More" size="icon-xs">
                <LuEllipsis />
              </InputGroupButton>
            </DropdownMenuTrigger>
            <DropdownMenuContent align="end">
              <DropdownMenuItem>Settings</DropdownMenuItem>
              <DropdownMenuItem>Copy path</DropdownMenuItem>
              <DropdownMenuItem>Open location</DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
        </InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupInput placeholder="Enter search query" />
        <InputGroupAddon align="inline-end">
          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <InputGroupButton variant="ghost" css={{ pr: "1.5!", textStyle: "xs" }}>
                Search In... <LuChevronDown className={css({ w: "3", h: "3" })} />
              </InputGroupButton>
            </DropdownMenuTrigger>
            <DropdownMenuContent align="end">
              <DropdownMenuItem>Documentation</DropdownMenuItem>
              <DropdownMenuItem>Blog Posts</DropdownMenuItem>
              <DropdownMenuItem>Changelog</DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
        </InputGroupAddon>
      </InputGroup>
    </styled.div>
  );
}

Button Group

Wrap input groups with button groups to create prefixes and suffixes.

.com
import { LuLink2 } from "react-icons/lu";
import { styled } from "styled-system/jsx";
import { ButtonGroup, ButtonGroupText } from "@/components/ui/button-group";
import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";
import { Label } from "@/components/ui/label";

export default function InputGroupButtonGroup() {
  return (
    <styled.div css={{ display: "grid", w: "full", maxW: "sm", gap: "6" }}>
      <ButtonGroup>
        <ButtonGroupText asChild>
          <Label htmlFor="url">https://</Label>
        </ButtonGroupText>
        <InputGroup>
          <InputGroupInput id="url" />
          <InputGroupAddon align="inline-end">
            <LuLink2 />
          </InputGroupAddon>
        </InputGroup>
        <ButtonGroupText>.com</ButtonGroupText>
      </ButtonGroup>
    </styled.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 className prop.

"use client";

import { styled } from "styled-system/jsx";
import { InputGroup, InputGroupAddon, InputGroupButton } from "@/components/ui/input-group";

export default function InputGroupCustom() {
  return (
    <styled.div css={{ display: "grid", w: "full", maxW: "sm", gap: "6" }}>
      <InputGroup>
        <styled.textarea
          placeholder="Autoresize textarea..."
          css={{
            display: "flex",
            fieldSizing: "content",
            minH: "16",
            w: "full",
            resize: "none",
            rounded: "md",
            bg: "transparent",
            px: "3",
            py: "2.5",
            textStyle: "md",
            transition: "common",
            outline: "none",
            md: { textStyle: "sm" },
          }}
        />
        <InputGroupAddon align="block-end">
          <InputGroupButton css={{ ml: "auto" }} size="sm">
            Submit
          </InputGroupButton>
        </InputGroupAddon>
      </InputGroup>
    </styled.div>
  );
}
import TextareaAutosize from "react-textarea-autosize";
import { InputGroup, InputGroupAddon } from "@/component/ui/input-group";

export function InputGroupCustom() {
  return (
    <InputGroup>
      <TextareaAutosize
        data-slot="input-group-control"
        className="dark:bg-input/30 flex field-sizing-content min-h-16 w-full resize-none rounded-md bg-transparent px-3 py-2 text-base transition-[color,box-shadow] outline-none"
        placeholder="Autoresize textarea..."
      />
      <InputGroupAddon align="block-end">how</InputGroupAddon>
    </InputGroup>
  );
}

API Reference

InputGroup

The main component that wraps inputs and addons.

PropTypeDefault
cssSystemStyleObject
<InputGroup>
  <InputGroupInput />
  <InputGroupAddon />
</InputGroup>

InputGroupAddon

Displays icons, text, buttons, or other content alongside inputs.

For proper focus navigation, the `InputGroupAddon` component should be placed after the input. Set the `align` prop to position the addon.
PropTypeDefault
align"inline-start" | "inline-end" | "block-start" | "block-end""inline-start"
cssSystemStyleObject
<InputGroupAddon align="inline-end">
  <SearchIcon />
</InputGroupAddon>

For <InputGroupInput />, use the inline-start or inline-end alignment. For <InputGroupTextarea />, use the block-start or block-end alignment.

The InputGroupAddon component can have multiple InputGroupButton components and icons.

<InputGroupAddon>
  <InputGroupButton>Button</InputGroupButton>
  <InputGroupButton>Button</InputGroupButton>
</InputGroupAddon>

InputGroupButton

Displays buttons within input groups.

PropTypeDefault
size"xs" | "icon-xs" | "sm" | "icon-sm""xs"
variant"default" | "destructive" | "outline" | "secondary" | "ghost" | "link""ghost"
cssSystemStyleObject
<InputGroupButton>Button</InputGroupButton>
<InputGroupButton size="icon-xs" aria-label="Copy">
  <CopyIcon />
</InputGroupButton>

InputGroupInput

Replacement for <Input /> when building input groups. This component has the input group styles pre-applied and uses the unified data-slot="input-group-control" for focus state handling.

PropTypeDefault
cssSystemStyleObject

All other props are passed through to the underlying <Input /> component.

<InputGroup>
  <InputGroupInput placeholder="Enter text..." />
  <InputGroupAddon>
    <SearchIcon />
  </InputGroupAddon>
</InputGroup>

InputGroupTextarea

Replacement for <Textarea /> when building input groups. This component has the textarea group styles pre-applied and uses the unified data-slot="input-group-control" for focus state handling.

PropTypeDefault
cssSystemStyleObject

All other props are passed through to the underlying <Textarea /> component.

<InputGroup>
  <InputGroupTextarea placeholder="Enter message..." />
  <InputGroupAddon align="block-end">
    <InputGroupButton>Send</InputGroupButton>
  </InputGroupAddon>
</InputGroup>