Input Group

Floating-label form fields with optional prefix and suffix icons, inline errors, and a companion wrapper for stacked input sets.

Live Playground

Use the grouped fields to inspect the floating label motion, focus underline, destructive state, and suffix-button behavior in one compact form.

Try the password suffix action and enter an incomplete email to inspect the floating label, inline error, and focus rule together.

Install And Iterate

Install the component directly into your codebase, then branch into v0 if you want to iterate on variations.

Install

npx shadcn@latest add @iconiq/input-group

Build with v0

Send the registry bundle into v0 when you want to explore new colorways, copy, or layout directions quickly.

Notes

  • Each field accepts native input props directly, so controlled and uncontrolled patterns both fit cleanly.
  • The grouped wrapper is optional, but it keeps multi-field forms spaced consistently without extra layout code.

Usage

Use Inputgroups for each field, then wrap several fields in InputGroup when you want the same vertical spacing across a compact form section.

"use client"; import { Eye, EyeOff, LockKeyhole, Mail, User2 } from "lucide-react";import { useState } from "react";import { InputGroup, Inputgroups } from "@/components/ui/input-group"; export function ContactFields() {  const [name, setName] = useState("");  const [email, setEmail] = useState("");  const [showPassword, setShowPassword] = useState(false);   const emailError =    email.length > 0 && !email.includes("@")      ? "Enter a valid email address."      : undefined;   return (    <InputGroup className="max-w-xl">      <Inputgroups        label="Full name"        onChange={(event) => setName(event.target.value)}        prefixIcon={<User2 aria-hidden className="size-5" />}        value={name}      />       <Inputgroups        error={emailError}        label="Work email"        onChange={(event) => setEmail(event.target.value)}        prefixIcon={<Mail aria-hidden className="size-5" />}        type="email"        value={email}      />       <Inputgroups        label="Password"        prefixIcon={<LockKeyhole aria-hidden className="size-5" />}        suffixIcon={          showPassword ? (            <EyeOff aria-hidden className="size-5" />          ) : (            <Eye aria-hidden className="size-5" />          )        }        onSuffixClick={() => setShowPassword((current) => !current)}        type={showPassword ? "text" : "password"}      />    </InputGroup>  );}

API Details

Each item below covers the documented props and the behavior that matters during implementation.

Inputgroups

7 props

Floating-label input field with optional prefix and suffix slots, a center-out focus rule, and inline error messaging driven entirely by props.
labelstring
Required
Field label rendered inside the shell until the input becomes focused or contains a value, then animated upward into its floating position.
errorstring
Optional validation message rendered below the field. Passing a value also switches the label and underline to the destructive palette.
prefixIconReactNode
Leading visual placed before the input area. The slot is best suited to compact icons and inherits the same focus-aware color shift as the suffix.
suffixIconReactNode
Optional trailing visual rendered inside a button, which makes it useful for actions such as show-password toggles or clear controls.
onSuffixClick() => void
Called when the suffix button is pressed. It only matters when suffixIcon is also present.
idstring
Optional input id. When omitted, the component creates one with useId and links the animated label automatically.
classNamestring
Applied to the native input element itself, not the outer shell. Use it for text sizing, placeholder, or input-level spacing overrides.

Notes

Standard native input props such as name, type, value, defaultValue, placeholder, autoComplete, disabled, onFocus, onBlur, and onChange are forwarded directly to the underlying input element.
The outer field shell uses a mouse-down focus handoff, so clicking the label area or prefix icon focuses the input without stealing focus from the suffix button.
Value presence is tracked from the real input value, which keeps the floating label aligned for both controlled and uncontrolled usage.

InputGroup

2 props

Lightweight vertical wrapper for stacking multiple Inputgroups fields with consistent spacing.
childrenReactNode
Required
One or more Inputgroups fields, or any other custom content you want to keep in the same vertical flow.
classNamestring
Merged onto the wrapper div. The base layout already uses a full-width flex column with a 1.5rem gap.

Notes

All remaining div props are forwarded to the wrapper, so data attributes and layout helpers can be attached at the group level.