Presets

Blocks

Get PRO

Need premium blocks and templates? Upgrade to PRO and get access.

Upgrade
JoinLogin
Presets
Monochrome
Overview
  • Introduction
  • Components
  • Installation
Components
  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Avatar
  • Badge
  • Breadcrumb
  • Button
  • Button Group
  • CalendarNEW
  • Card
  • CarouselNEW
  • Checkbox
  • Collapsible
  • Dialog
  • Dropdown Menu
  • Empty
  • Field
  • Input
  • Input Group
  • Item
  • Kbd
  • Label
  • Native Select
  • Scroll Area
  • SelectNEW
  • Separator
  • SwitchNEW
  • Table
  • Tabs
  • Textarea
  • TooltipNEW

Input Group

PreviousNext

Add addons, buttons, and helper content to inputs.

1import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";2import { Search } from "lucide-react";34export default function Preview() {

Installation

pnpm dlx sprawlify@latest add input-group
npx sprawlify@latest add input-group
yarn sprawlify@latest add input-group
bunx --bun sprawlify@latest add input-group

Install the following dependencies:

pnpm add @sprawlify/primitives @sprawlify/react
npm install @sprawlify/primitives @sprawlify/react
yarn add @sprawlify/primitives @sprawlify/react
bun add @sprawlify/primitives @sprawlify/react

Add the following files to your project:

input-group.tsx
1"use client";23import * as React from "react";4import { cva, type VariantProps } from "class-variance-authority";5import { cn } from "@/lib/utils";6import { Button } from "@/components/ui/button";7import { Input } from "@/components/ui/input";8import { Textarea } from "@/components/ui/textarea";9import { sprawlify } from "@sprawlify/react";1011function InputGroup({ className, ...props }: React.ComponentProps<typeof sprawlify.div>) {12  return (13    <sprawlify.div14      data-scope="input-group"15      data-part="root"16      data-slot="input-group"17      role="group"18      className={cn(19        "border-input bg-input/30 dark:bg-input/30 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-disabled:bg-input/50 dark:has-disabled:bg-input/80 h-8 rounded-lg border transition-colors in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-3 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5 group/input-group relative flex w-full min-w-0 items-center outline-none has-[>textarea]:h-auto",20        className,21      )}22      {...props}23    />24  );25}2627const inputGroupAddonVariants = cva(28  "text-muted-foreground h-auto gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4 flex cursor-text items-center justify-center select-none",29  {30    variants: {31      align: {32        "inline-start": "pl-2 has-[>button]:ml-[-0.3rem] has-[>kbd]:ml-[-0.15rem] order-first",33        "inline-end": "pr-2 has-[>button]:mr-[-0.3rem] has-[>kbd]:mr-[-0.15rem] order-last",34        "block-start":35          "px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2 order-first w-full justify-start",36        "block-end":37          "px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2 order-last w-full justify-start",38      },39    },40    defaultVariants: {41      align: "inline-start",42    },43  },44);4546function InputGroupAddon({47  className,48  align = "inline-start",49  ...props50}: React.ComponentProps<typeof sprawlify.div> & VariantProps<typeof inputGroupAddonVariants>) {51  return (52    <sprawlify.div53      role="group"54      data-scope="input-group"55      data-part="addon"56      data-slot="input-group-addon"57      data-align={align}58      className={cn(inputGroupAddonVariants({ align }), className)}59      onClick={(e) => {60        if ((e.target as HTMLElement).closest("button")) {61          return;62        }63        e.currentTarget.parentElement?.querySelector("input")?.focus();64      }}65      {...props}66    />67  );68}6970const inputGroupButtonVariants = cva("gap-2 text-sm shadow-none flex items-center", {71  variants: {72    size: {73      xs: "h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5",74      sm: "",75      "icon-xs": "size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0",76      "icon-sm": "size-8 p-0 has-[>svg]:p-0",77    },78  },79  defaultVariants: {80    size: "xs",81  },82});8384function InputGroupButton({85  className,86  type = "button",87  variant = "ghost",88  size = "xs",89  ...props90}: Omit<React.ComponentProps<typeof Button>, "size"> &91  VariantProps<typeof inputGroupButtonVariants>) {92  return (93    <Button94      data-scope="input-group"95      data-part="button"96      type={type}97      data-size={size}98      variant={variant}99      className={cn(inputGroupButtonVariants({ size }), className)}100      {...props}101    />102  );103}104105function InputGroupText({ className, ...props }: React.ComponentProps<typeof sprawlify.span>) {106  return (107    <sprawlify.span108      data-scope="input-group"109      data-part="text"110      data-slot="input-group-text"111      className={cn(112        "text-muted-foreground gap-2 text-sm [&_svg:not([class*='size-'])]:size-4 flex items-center [&_svg]:pointer-events-none",113        className,114      )}115      {...props}116    />117  );118}119120function InputGroupInput({ className, ...props }: React.ComponentProps<typeof sprawlify.input>) {121  return (122    <Input123      data-scope="input-group"124      data-part="control"125      data-slot="input-group-control"126      className={cn(127        "rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-input/50 aria-invalid:ring-0 dark:bg-input/30 dark:disabled:bg-input/50 flex-1",128        className,129      )}130      {...props}131    />132  );133}134135function InputGroupTextarea({136  className,137  ...props138}: React.ComponentProps<typeof sprawlify.textarea>) {139  return (140    <Textarea141      data-scope="input-group"142      data-part="control"143      data-slot="input-group-control"144      className={cn(145        "rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-input/50 aria-invalid:ring-0 dark:bg-input/30 dark:disabled:bg-transparent flex-1 resize-none",146        className,147      )}148      {...props}149    />150  );151}152153export {154  InputGroup,155  InputGroupAddon,156  InputGroupButton,157  InputGroupText,158  InputGroupInput,159  InputGroupTextarea,160};161

Update the import paths to match your project setup.

Usage

1import {
2  InputGroup,
3  InputGroupAddon,
4  InputGroupButton,
5  InputGroupText,
6  InputGroupInput,
7  InputGroupTextarea
8} from "@/components/ui/input-group"
1<InputGroup>
2  <InputGroupInput placeholder="Search..." />
3  <InputGroupAddon>
4    <SearchIcon />
5  </InputGroupAddon>
6</InputGroup>

Examples

Block End

Use align="block-end" to position the addon below the input.

1import { Field, FieldDescription, FieldGroup, FieldLabel } from "@/components/ui/field";2import {3  InputGroup,4  InputGroupAddon,

Block Start

Use align="block-start" to position the addon above the input.

1import { Field, FieldDescription, FieldGroup, FieldLabel } from "@/components/ui/field";2import {3  InputGroup,4  InputGroupAddon,

Dropdown

1import {2  DropdownMenu,3  DropdownMenuContent,4  DropdownMenuGroup,

Icon

1import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";2import { CheckIcon, CreditCardIcon, InfoIcon, MailIcon, SearchIcon, StarIcon } from "lucide-react";34export default function Preview() {

Inline End

Use align="inline-end" to position the addon at the end of the input.

1import { Field, FieldDescription, FieldLabel } from "@/components/ui/field";2import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";3import { EyeOffIcon } from "lucide-react";4

Inline Start

Use align="inline-start" to position the addon at the start of the input. This is the default.

1import { Field, FieldDescription, FieldLabel } from "@/components/ui/field";2import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";3import { SearchIcon } from "lucide-react";4

Kbd

1import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";2import { Kbd } from "@/components/ui/kbd";3import { SearchIcon } from "lucide-react";4

Text

1import {2  InputGroup,3  InputGroupAddon,4  InputGroupInput,

Textarea

1import {2  InputGroup,3  InputGroupAddon,4  InputGroupButton,

On This Page

InstallationUsageExamplesBlock EndBlock StartDropdownIconInline EndInline StartKbdTextTextarea

Get PRO

Need premium blocks and templates? Upgrade to PRO and get access.