Skip to content

Now in the official shadcn registry directory.

Start free

File Input

A styled file picker over a hidden native input. 3 variants, 3 sizes, 3 tones, a leading icon, selected file name and an optional clear button.

form
A free styled version is available

Installation

Run the following command

npx shadcn@latest add @ai2/file-input

Dependencies, the @ai2/tokens theme and the component file are installed together.

Usage

TS
import { FileInput } from "@/components/ui/file-input"

<FileInput aria-label="Upload attachment" />

All three axes are optional. Omit them and you get the outline medium neutral picker. Native file input props such as multiple, accept and disabled pass through to the hidden input.

Examples

Variants

Pick a file to see the selected state: the placeholder swaps to the file name and a clear button appears at the trailing edge.

Sizes

Tones

The tone axis colors the border and focus ring with the success or danger token, giving controlled validation feedback without touching aria-invalid.

Invalid state

Set aria-invalid on validation errors. It lands on the hidden input and the danger border lifts to the visible surface automatically.

Disabled

disabled lands on the hidden input; the visible surface halves its opacity and stops receiving pointer events via has-[] selectors.

Custom icon

The icon prop replaces the default Paperclip with any node. Remixicon works too: icon={<i className="ri-image-line" />} renders at the right size without extra classes.

Multiple and accept

With multiple, selecting more than one file shows a count such as 3 files instead of a name. accept filters the file dialog to matching types.

Props

FileInput also accepts every native <input> prop except size (repurposed for the size axis) and type (always file). They are forwarded to the hidden native input.

PropTypeDefaultDescription
variant"outline" | "soft" | "ghost""outline"Visual style of the picker surface.
size"sm" | "md" | "lg""md"Height, padding, radius and typography scale. Replaces the native size attribute.
tone"neutral" | "success" | "danger""neutral"Semantic validation color: success and danger recolor the border and focus ring.
placeholderstring"Choose a file"Text shown before a file is picked.
iconReact.ReactNodelucide PaperclipLeading icon. Accepts any node, including a remixicon <i> element; sizing for both is built in.
clearablebooleantrueShows a clear button once a file is selected. Clearing dispatches a bubbling change event.

ai2 File Input: a styled react file picker with variants and tones

The ai2 File Input is a shadcn-compatible file picker component for React, styled with Tailwind CSS v4 on the shared ai2 tokens. It hides the native file input under a custom surface and follows the same axis contract as the ai2 Input: 3 variants (outline, soft, ghost), 3 sizes (sm, md, lg) and 3 tones (neutral, success, danger), plus a leading icon, a placeholder, the selected file name and an optional clear button.

Because it ships through the shadcn registry format, you install it with one CLI command, an MCP agent, or a copy-paste, and the source lands in your own project. You own the file; there is no runtime dependency on ai2 itself. The live example above is the exact component you get.

What is the ai2 File Input?

It is a styled <label> wrapping a visually hidden native <input type="file">, driven by fileInputVariants, an exported cva definition with a variant axis (outline, soft, ghost), a size axis (sm, md, lg) and a tone axis (neutral, success, danger). Clicking anywhere on the surface opens the file dialog because the label owns the input.

The component tracks selection itself: before a pick it shows the placeholder, after a pick it shows the file name (or a "N files" count with multiple), and it exposes data-state="empty" or data-state="selected" for CSS hooks. Focus, disabled and aria-invalid styles lift from the hidden input to the visible surface via has-[] selectors.

Why use it

  • Native input, custom surface: The real file input stays in the DOM (visually hidden), so forms, FormData, the accept and multiple attributes and screen reader semantics keep working while the visible surface matches your theme.
  • Same axes as the ai2 Input: 3 variants times 3 sizes times 3 tones from one cva definition, matching the Input contract, so file pickers sit visually flush inside any ai2 form.
  • Selection feedback built in: Placeholder before a pick, the file name after, a file count for multiple selections, and a clear button that resets the input and dispatches a bubbling change event.
  • Icon flexibility: The leading icon defaults to a lucide Paperclip and accepts any node, including remixicon <i> elements; sizing rules for both svg and i children are in the component.
  • Agent-readable metadata: The registry item states the variant matrix and states in plain words, so an MCP agent can pick the right combination without reading the source.

Features

  • shadcn registry install: One command adds the component and the @ai2/tokens theme to your project.
  • 3 variants, 3 sizes, 3 tones: outline (default), soft and ghost, each in sm, md and lg with matched height, padding, radius and typography, plus neutral, success and danger tones for validation color.
  • State lifted from the hidden input: focus-visible ring, disabled opacity and aria-invalid danger border all read from the native input via has-[] selectors, so the visible surface always reflects the real control.
  • Clear button with a real change event: Clearing resets the input value and dispatches a bubbling change event, so form libraries listening on the input stay in sync.
  • Multiple file count: With the multiple attribute, picking more than one file shows a compact count such as 3 files instead of a truncated name list.
  • Data attributes for styling: The root exposes data-slot="file-input", data-variant, data-tone and data-state, and each part has its own data-slot, so you can restyle from CSS without forking.

Production tips

  • Constrain uploads with accept: Pass the native accept attribute (for example image/*) so the file dialog filters to the right types; validation still belongs on the server.
  • Use tone or aria-invalid for errors: aria-invalid flips the border to the danger color on its own; the danger tone also recolors the focus ring. Use aria-invalid for form-library errors and tone for controlled validation looks.
  • Give it an accessible name: The visible text is the placeholder or the picked file name, not a label. Pair it with an ai2 Label or Field, or set aria-label on the component.
  • Keep clearable for optional uploads: The clear button is the only built-in way for users to unselect a file without picking another one. Only set clearable={false} when an upload is mandatory.
  • Read files from the change event: The component does not hold a files prop; read event.target.files in onChange exactly as with a bare native input.

Works with the rest of ai2

Wrap it in an ai2 Field to get label, description and error placement for free, or pair it with an ai2 Label directly, and submit with an ai2 Button.

It shares the variant, size and tone language with the Input and Textarea, so mixed forms stay visually consistent. If you only need the browser default file button restyled, the Input also styles a bare type="file".

FAQs

Get new components in your inbox

Component drops, new themes and release notes. No spam, and you can unsubscribe from any email.

© 2026 ai2 design system · MIT licensed · Built with its own components