Capítulo 22 de 108

Input

Core Idea

Input is a styled wrapper around the native <input> element. It has no special props of its own beyond native HTML input attributes; all structure (label, description, validation, disabled/invalid styling) comes from composing it with Field.

Key Concepts

  • No custom API: Input accepts standard HTML <input> props (type, placeholder, disabled, required, aria-invalid, etc.) directly, there's no shadcn-specific prop table.
  • Disabled styling: set disabled on Input itself, and add data-disabled on the wrapping Field to style the whole field (label + description) as disabled.
  • Invalid styling: set aria-invalid on Input, and add data-invalid on the wrapping Field to style the whole field as invalid (this mirrors the Field component's own convention).
  • type="file": works like any native file input, styled consistently.
  • Inline layout: Field orientation="horizontal" pairs an Input with a Button (e.g. search bar with submit button).
  • Grid layout: FieldGroup className="grid ..." places multiple Fields (each with its own Input) side by side.
  • Icons/prefix/suffix inside the input: not done via Input props, use the separate InputGroup component (InputGroupInput, InputGroupAddon, InputGroupText).

Code Examples

<Field>
  <FieldLabel htmlFor="input-demo-api-key">API Key</FieldLabel>
  <Input id="input-demo-api-key" type="password" placeholder="sk-..." />
  <FieldDescription>
    Your API key is encrypted and stored securely.
  </FieldDescription>
</Field>
  • O que demonstra: padrão canônico Input + Field + FieldLabel + FieldDescription.
<Field data-invalid>
  <FieldLabel htmlFor="input-invalid">Invalid Input</FieldLabel>
  <Input id="input-invalid" placeholder="Error" aria-invalid />
  <FieldDescription>This field contains validation errors.</FieldDescription>
</Field>
  • O que demonstra: par aria-invalid (no Input) + data-invalid (no Field) para estado de erro estilizado.
<Field orientation="horizontal">
  <Input type="search" placeholder="Search..." />
  <Button>Search</Button>
</Field>
  • O que demonstra: input inline com botão, via Field orientation="horizontal".
<Field>
  <FieldLabel htmlFor="input-group-url">Website URL</FieldLabel>
  <InputGroup>
    <InputGroupInput id="input-group-url" placeholder="example.com" />
    <InputGroupAddon>
      <InputGroupText>https://</InputGroupText>
    </InputGroupAddon>
    <InputGroupAddon align="inline-end">
      <InfoIcon />
    </InputGroupAddon>
  </InputGroup>
</Field>
  • O que demonstra: prefixo de texto e ícone dentro do input via InputGroup (não é feature nativa do Input).

Anti-patterns

  • Expecting Input to accept icon/prefix/addon props: it doesn't, reach for InputGroup instead.
  • Styling invalid/disabled state only on Input: also set data-invalid/data-disabled on the parent Field so the label/description visually match.

Key Takeaways

  1. Input is intentionally minimal, all form composition patterns (label, error, grouping, layout) live in Field/FieldGroup, not in Input itself.
  2. Use a Badge inside FieldLabel to flag a field as e.g. "Beta" or recommended.
  3. For a full multi-field form, FieldGroup wraps multiple Fields (including Select) plus a final horizontal Field for submit/reset buttons.

Connects To

  • components-field: the composition system Input always pairs with.
  • components-input-group: for icons/text/buttons embedded inside the input itself.
  • components-input-otp: separate component for one-time-code style multi-box input.