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";23function SearchIcon() {4  return (

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/solid
npm install @sprawlify/primitives @sprawlify/solid
yarn add @sprawlify/primitives @sprawlify/solid
bun add @sprawlify/primitives @sprawlify/solid

Add the following files to your project:

input-group.tsx
1import { cva, type VariantProps } from "class-variance-authority";2import { cn } from "@/lib/utils";3import { Button } from "@/components/ui/button";4import { Input } from "@/components/ui/input";5import { Textarea } from "@/components/ui/textarea";6import { sprawlify } from "@sprawlify/solid";7import { type ComponentProps, splitProps } from "solid-js";89function InputGroup(props: ComponentProps<typeof sprawlify.div>) {10  const [local, others] = splitProps(props, ["class"]);1112  return (13    <sprawlify.div14      data-scope="input-group"15      data-part="root"16      data-slot="input-group"17      role="group"18      class={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        local.class,21      )}22      {...others}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  props: ComponentProps<typeof sprawlify.div> & VariantProps<typeof inputGroupAddonVariants>,48) {49  const [local, others] = splitProps(props, ["class", "align"]);50  const align = () => local.align ?? "inline-start";5152  return (53    <sprawlify.div54      role="group"55      data-scope="input-group"56      data-part="addon"57      data-slot="input-group-addon"58      data-align={align()}59      class={cn(inputGroupAddonVariants({ align: align() }), local.class)}60      onClick={(e) => {61        if ((e.target as HTMLElement).closest("button")) {62          return;63        }64        e.currentTarget.parentElement?.querySelector("input")?.focus();65      }}66      {...others}67    />68  );69}7071const inputGroupButtonVariants = cva("gap-2 text-sm shadow-none flex items-center", {72  variants: {73    size: {74      xs: "h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5",75      sm: "",76      "icon-xs": "size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0",77      "icon-sm": "size-8 p-0 has-[>svg]:p-0",78    },79  },80  defaultVariants: {81    size: "xs",82  },83});8485function InputGroupButton(86  props: Omit<ComponentProps<typeof Button>, "size"> &87    VariantProps<typeof inputGroupButtonVariants>,88) {89  const [local, others] = splitProps(props, ["class", "type", "variant", "size"]);90  const type = () => local.type ?? "button";91  const variant = () => local.variant ?? "ghost";92  const size = () => local.size ?? "xs";9394  return (95    <Button96      data-scope="input-group"97      data-part="button"98      type={type()}99      data-size={size()}100      variant={variant()}101      class={cn(inputGroupButtonVariants({ size: size() }), local.class)}102      {...others}103    />104  );105}106107function InputGroupText(props: ComponentProps<typeof sprawlify.span>) {108  const [local, others] = splitProps(props, ["class"]);109110  return (111    <sprawlify.span112      data-scope="input-group"113      data-part="text"114      data-slot="input-group-text"115      class={cn(116        "text-muted-foreground gap-2 text-sm [&_svg:not([class*='size-'])]:size-4 flex items-center [&_svg]:pointer-events-none",117        local.class,118      )}119      {...others}120    />121  );122}123124function InputGroupInput(props: ComponentProps<typeof sprawlify.input>) {125  const [local, others] = splitProps(props, ["class"]);126127  return (128    <Input129      data-scope="input-group"130      data-part="control"131      data-slot="input-group-control"132      class={cn(133        "rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent flex-1",134        local.class,135      )}136      {...others}137    />138  );139}140141function InputGroupTextarea(props: ComponentProps<typeof sprawlify.textarea>) {142  const [local, others] = splitProps(props, ["class"]);143144  return (145    <Textarea146      data-scope="input-group"147      data-part="control"148      data-slot="input-group-control"149      class={cn(150        "rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent flex-1",151        local.class,152      )}153      {...others}154    />155  );156}157158export {159  InputGroup,160  InputGroupAddon,161  InputGroupButton,162  InputGroupText,163  InputGroupInput,164  InputGroupTextarea,165};166

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-solid";34export default function Preview() {

Inline End

1import { Field, FieldDescription, FieldLabel } from "@/components/ui/field";2import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";3import { EyeOffIcon } from "lucide-solid";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";34function SearchIcon() {

Kbd

1import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";2import { Kbd } from "@/components/ui/kbd";3import { SearchIcon } from "lucide-solid";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.