Files
skills/docs/ui/react/react-ui/src/components/input-group/input-group.md
2026-05-29 08:20:56 +01:00

7.8 KiB

InputGroup

(no submodules)

- input-group.variants — `inputGroupVariants`, `input…
  • input-group.variants — inputGroupVariants, inputGroupAddonVariants, inputGroupInputVariants, inputGroupTextareaVariants, and `inputGro…

  • input-group.variants — inputGroupVariants, `input…

  • input-group.variants — inputGroupVariants, inputGroupAddonVariants, inputGroupInputVariants, inputGroupTextareaVariants, and `inputGro…

  • input-group.variants — inputGroupVariants, `input…

  • input-group.variants — inputGroupVariants, inputGroupAddonVariants, inputGroupInputVariants, inputGroupTextareaVariants, and `inputGro…

  • input-group.variants — inputGroupVariants, `input…

  • input-group.variants — inputGroupVariants, inputGroupAddonVariants, inputGroupInputVariants, inputGroupTextareaVariants, and `inputGro…

  • input-group.variants — inputGroupVariants, inputGroupAddonVariants, inputGroupInputVariants, inputGroupTextareaVariants, and `inputGro…

Purpose

<InputGroup> is the chassis container that wraps an input alongside leading/trailing addons (icons, prefix/suffix text, inline buttons). It hoists the focus ring and invalid border onto the wrapper itself so the inner control can drop its own ring; the dedicated <InputGroupInput> and <InputGroupTextarea> primitives strip the standard border / shadow / focus ring so the wrapper carries them.

Per the shadcn skill rule, NEVER drop a raw <Input> or <Textarea> inside <InputGroup> — the inner control needs the wrapped surface variants to remove its own border and focus ring. The chassis provides <InputGroupInput> and <InputGroupTextarea> for the input case. Inline buttons inside inputs use <InputGroupAddon position="none"> + <InputGroupButton> (renders an icon-button without a separator border).

Features

  • Wrapper container shares focus ring + invalid border with every child via focus-within: and data-[invalid=true]:.
  • <InputGroupAddon> with three positions (start / end / none) controlling which separator border is drawn.
  • <InputGroupInput> strips the standard <Input> border / shadow / focus ring.
  • <InputGroupTextarea> strips the standard <Textarea> border / shadow / focus ring.
  • <InputGroupButton> for inline icon-buttons (toggle password visibility, clear input, attach file).
  • placeholder on <InputGroupInput> / <InputGroupTextarea> typed as TranslationKey | undefined and resolved via active i18n.
  • data-invalid="true" and data-disabled="true" on the wrapper drive cva selectors.
  • Refs forwarded on every primitive.
  • motion-reduce:transition-none collapses transitions for prefers-reduced-motion: reduce.
  • Theme-driven variant overrides via useThemedVariants("InputGroup" | "InputGroupAddon" | "InputGroupInput" | "InputGroupTextarea" | "InputGroupButton").

Usage: Basic Example

import {
    InputGroup,
    InputGroupAddon,
    InputGroupInput,
} from "@suaveplan/react-ui";

export function EmailField() {
    return (
        <InputGroup>
            <InputGroupAddon>@</InputGroupAddon>
            <InputGroupInput type="email" />
        </InputGroup>
    );
}

Usage: Advanced Example

import {
    InputGroup,
    InputGroupAddon,
    InputGroupButton,
    InputGroupInput,
} from "@suaveplan/react-ui";
import { EyeIcon, EyeOffIcon } from "lucide-react";
import { useState } from "react";

export function PasswordWithReveal() {
    const [show, setShow] = useState(false);
    const [pwd, setPwd] = useState("");
    const tooShort = pwd.length > 0 && pwd.length < 8;

    return (
        <InputGroup invalid={tooShort}>
            <InputGroupAddon>Pwd</InputGroupAddon>
            <InputGroupInput
                type={show ? "text" : "password"}
                value={pwd}
                aria-invalid={tooShort}
                onChange={(e) => setPwd(e.currentTarget.value)}
            />
            <InputGroupAddon position="none">
                <InputGroupButton
                    aria-label={show ? "Hide password" : "Show password"}
                    onClick={() => setShow((s) => !s)}
                >
                    {show ? <EyeOffIcon /> : <EyeIcon />}
                </InputGroupButton>
            </InputGroupAddon>
        </InputGroup>
    );
}

API Reference

<InputGroup>

forwardRef<HTMLDivElement, InputGroupProps> — renders a <div> wrapper.

InputGroupProps

Prop Type Default Description
invalid boolean false Drives data-invalid="true" on the wrapper so border + focus ring flip to destructive.
disabled boolean false Drives data-disabled="true" for opacity decay. Pass to inner <InputGroupInput> separately for native semantics.
all native HTMLAttributes<HTMLDivElement> native native Forwarded as-is.

<InputGroupAddon>

forwardRef<HTMLDivElement, InputGroupAddonProps> — renders a <div> for decorative content.

InputGroupAddonProps

Prop Type Default Description
position "start" | "end" | "none" "start" Which side the addon hugs and which separator border is drawn (none = no separator, used for inline buttons).

<InputGroupInput>

forwardRef<HTMLInputElement, InputGroupInputProps> — renders an <input> with the standard border / shadow / focus ring stripped.

InputGroupInputProps

Inherits every prop from InputHTMLAttributes<HTMLInputElement> minus size / placeholder. Adds:

Prop Type Default Description
placeholder TranslationKey none Resolved via active i18n.

<InputGroupTextarea>

forwardRef<HTMLTextAreaElement, InputGroupTextareaProps> — renders a <textarea> with the standard border / shadow / focus ring stripped.

InputGroupTextareaProps

Inherits every prop from TextareaHTMLAttributes<HTMLTextAreaElement> minus placeholder. Adds:

Prop Type Default Description
placeholder TranslationKey none Resolved via active i18n.

<InputGroupButton>

forwardRef<HTMLButtonElement, InputGroupButtonProps> — renders a small icon-button with type="button" by default so it does not accidentally submit the surrounding form.

InputGroupButtonProps

Inherits every prop from ButtonHTMLAttributes<HTMLButtonElement>. The default type="button" can be overridden if intentional.

Cva resolvers

inputGroupVariants, inputGroupAddonVariants, inputGroupInputVariants, inputGroupTextareaVariants, and inputGroupButtonVariants are all exported as bare cva resolvers for external composition.

Implementation Notes

  • Per the shadcn skill rule, the chassis enforces the <InputGroupInput> / <InputGroupTextarea> wrappers — raw <Input> / <Textarea> inside <InputGroup> carries an inner border + focus ring that double up with the wrapper's own.
  • The position="none" value on <InputGroupAddon> is the supported way to render an inline button without a separator border. The button supplies its own padding so the addon does not need one.
  • <InputGroupButton> defaults to type="button" so a button rendered inside an input group does not submit the surrounding form when clicked. Override with type="submit" only when intentional.
  • The wrapper carries the focus ring via focus-within: so any inner element gaining focus surfaces the ring on the wrapper. The inner <InputGroupInput> removes its own ring via focus:ring-0 to avoid the visual double-ring.
  • Refs target the underlying DOM element on every primitive — consumers can .focus() the input, button, addon, or wrapper programmatically.