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-groupnpx sprawlify@latest add input-groupyarn sprawlify@latest add input-groupbunx --bun sprawlify@latest add input-group
Install the following dependencies:
pnpm add @sprawlify/primitives @sprawlify/solidnpm install @sprawlify/primitives @sprawlify/solidyarn add @sprawlify/primitives @sprawlify/solidbun add @sprawlify/primitives @sprawlify/solid
Add the following files to your project:
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};166Update 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";4Inline 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";4Text
1import {2 InputGroup,3 InputGroupAddon,4 InputGroupInput,Textarea
1import {2 InputGroup,3 InputGroupAddon,4 InputGroupButton,On This Page
InstallationUsageExamplesBlock EndBlock StartDropdownIconInline EndInline StartKbdTextTextareaGet PRO
Need premium blocks and templates? Upgrade to PRO and get access.