Eunomia v1.1.0
AllAngularReactNext.jsJavaScriptVue
EunomiaSalmonForestVioletOceanGoldFireCustom…
🇬🇧 English🇫🇷 Français

Input file

Input file is a field for picking one or more files, by click or drag-and-drop, through a native picker wrapped in a custom dropzone. It can show selected files as a thumbnail grid or as a row list with an icon, name, and size. Each file can be removed on its own, all at once, or as part of a checked selection, and clicking a file opens it in a new tab.

Dependencies

eun-label · if it has a label eun-icon · for the dropzone and file icons
Overview API Examples Accessibility

When to use

Reach for an input file whenever someone needs to attach one or more files to a form, such as a profile picture, a set of product photos, a signed PDF, or supporting documents for a claim. It shows files as thumbnails by default, better suited to images, or as a compact row list for anything else, such as PDFs, spreadsheets, or other attachments. For a single free-text value, reach for input instead, since this component is only for actual files. See Alternatives below for a direct comparison.

Install & usage

Pick a framework in the toolbar above and these snippets adapt.

npm install @eunomia/elements
import "@eunomia/elements/input-file.js";
<eun-input-file name="photos" label="Photos" multiple></eun-input-file>

Importing the file registers <eun-input-file> as a custom element, with no further setup needed. It works with any framework, or none, since it's a standard web component.

Alternatives

You want… Prefers
A single free-text value, not actual files eun-input

Guidance

  • Set kind="file" whenever the accepted content isn't images: a row list with a name and size reads far better than a thumbnail grid for PDFs or documents
  • Set max-size whenever there's a real backend limit, so oversized files are rejected client-side instead of failing on submit
  • Only turn on selectable when someone plausibly needs to remove several files at once: for a handful of files, the per-item remove button is already enough
  • Using this component for a single free-text value: see Alternatives above
  • Relying on accept/max-size alone as a security boundary: always re-validate file type and size server-side
  • Overriding colors with inline styles instead of the --input-file-* CSS variables

Live testing

Properties

Input file <eun-input-file>

Attributes

NameTypeDefaultDescription
max-sizenumber | undefined—Maximum size, in bytes, accepted per file.
max-filesnumber | undefined—Maximum total number of files accepted. Only relevant with `multiple`.
drop-labelstring—Static text displayed in the dropzone. Defaults to the localized string for the current locale (English: "Drag and drop files here, or")
browse-labelstring—The visible text of the dropzone's browse link. Defaults to the localized string for the current locale (English: "browse files")
remove-labelstring—The accessible label of each item's remove button. Defaults to the localized string for the current locale (English: "Remove file")
select-labelstring—The accessible label prefix of each item's checkbox. Defaults to the localized string for the current locale (English: "Select file")
remove-all-labelstring—The label of the always-available bulk clear action. Defaults to the localized string for the current locale (English: "Remove all", or "Remove" without multiple)
select-all-labelstring—The label of the selection toolbar's select-all checkbox. Defaults to the localized string for the current locale (English: "Select all")
delete-selected-labelstring—The label of the selection toolbar's bulk delete button. Defaults to the localized string for the current locale (English: "Delete selected")
open-labelstring—Appended to each item's accessible name. Defaults to the localized string for the current locale (English: "Open in a new tab")
kind'image' | 'file''image'The accepted content kind: image thumbnails or generic file rows
acceptstring—The native accept filter, forwarded to the picker and used to filter drops. Defaults to images when kind is image, unset otherwise
multiplebooleanfalseWhether more than one file can be selected
selectablebooleanfalseShows per-item checkboxes, a select-all toggle, and a bulk delete action once more than one file is present

Import the exact TypeScript type behind any property above, see Types.

Properties

JS-only — no matching HTML attribute, set these from a script or a template binding.

NameTypeDefaultDescription
labelstring—The title label of the field, rendered through the label component
instructionsstring—Instructions displayed below the label
hideLabelbooleanfalseWhether the label is visually hidden. It still gives the field its accessible name, and the instructions stay visible
namestring—The field name
valueArray<File | string | {url: string, name?: string}>—The currently selected files. A string, or an object with a url and an optional name, is a file already stored, shown like the others and submitted as that URL
disabledbooleanfalseWhether the field is disabled
readonlybooleanfalseWhether the field is read only
requiredbooleanfalseWhether at least one file is required
hideErrorbooleanfalseWhether the errors are hidden. The hint stays visible
invalidbooleanfalseShows the field as invalid without any message, for an error displayed elsewhere
errorstring—An error computed outside the field, by the form or by the server. While set, the field is invalid and shows this message right away
validatorsArray<Validators<Array<File | string | {url: string, name?: string}>>>—The list of validation rules applied to the field value

Events

NameTypeDescription
eunchangeChangeEventFired whenever the selected files change
eunfilerejectFileRejectEventFired whenever one or more offered files are rejected, for the wrong type or exceeding the size or count limit

Every event above follows the same naming convention, covered in Events.

CSS custom properties

NameDescription
--input-file-border-colorSets the border color of the dropzone
--input-file-border-color-hoverSets the border color on hover
--input-file-border-color-focusSets the border color on focus
--input-file-border-radiusSets the corner radius of the dropzone and items
--input-file-backgroundSets the background color of the dropzone
--input-file-background-hoverSets the background color on hover or drag over
--input-file-text-colorSets the text color
--input-file-icon-colorSets the color of icons and action buttons
--input-file-error-colorSets the color of the error message
--input-file-hint-colorSets the color of the hint message
--input-file-focus-outline-colorSets the color of the focus outline
--input-file-item-backgroundSets the background color of each item
--input-file-item-border-colorSets the border color of each item
--input-file-gapSets the gap between the dropzone, header, and items
--input-file-thumbnail-sizeSets the minimum size of image thumbnails
--field-label-font-sizeSets the font size of the label
--field-message-font-sizeSets the font size of the hint, error messages and instructions

Basic

Image kind (default)

<eun-input-file name="avatar" label="Avatar" hint="PNG or JPG"></eun-input-file>

File kind

<eun-input-file
  name="document"
  label="Document"
  kind="file"
  accept="application/pdf"
>
</eun-input-file>

Several files

Multiple

With multiple, the dropzone stays visible above the item list/grid so more files can keep being added.

<eun-input-file name="gallery" label="Gallery" multiple></eun-input-file>

Selectable, with bulk delete

selectable adds a checkbox to each item, plus a select-all checkbox and a "Delete selected" action once at least one is checked, independent from the always-available "Remove all".

<eun-input-file
  name="attachments"
  label="Attachments"
  kind="file"
  multiple
  selectable
>
</eun-input-file>

Restricting size and count

max-size (bytes) rejects oversized files, while max-files caps the total count once multiple is set. Both emit eunfilereject instead of silently dropping the file, so listen for it to surface why a file didn't make it in.

No rejections yet.
<eun-input-file
  name="receipts"
  label="Receipts"
  kind="file"
  multiple
  max-size="2000000"
  max-files="3"
  hint="Up to 3 files, 2 MB each"
>
</eun-input-file>
const field = document.querySelector("#receipts");

field.addEventListener("eunfilereject", (event) => {
  const summary = event.files
    .map(({ file, reason }) => `${file.name}: ${reason}`)
    .join("\n");
  console.log(summary);
});

States

Disabled

<eun-input-file label="Disabled" disabled></eun-input-file>

Readonly

A readonly field still shows and submits its files, but nothing can be added or removed.

<eun-input-file label="Readonly" readonly></eun-input-file>

Required

<eun-input-file
  label="Proof of address"
  kind="file"
  required
  hint="A recent utility bill or bank statement"
>
</eun-input-file>

Forms and stored files

Forms

Like every other Eunomia field, eun-input-file is a real form-associated custom element, so the selected File objects themselves are submitted under its name, exactly like a native <input type="file">.

Submit Reset
[]
<form id="my-form">
  <eun-input-file name="attachments" label="Attachments" kind="file" multiple>
  </eun-input-file>
  <button type="submit">Submit</button>
  <button type="reset">Reset</button>
</form>
const form = document.querySelector("#my-form");

form.addEventListener("eunchange", () => {
  const files = new FormData(form).getAll("attachments");
  console.log(files.map((file) => file.name));
});

Stored files

To show a file that is already stored, put its URL in value next to, or instead of, File objects. It is displayed like any picked file, opens from that URL, and is submitted as that URL, so the server knows the file is kept. Its name is the last segment of the URL, unless you give it as { url, name }.

<eun-input-file id="logo" name="logo" label="Logo"></eun-input-file>
document.querySelector("#logo").value = [
  "https://cdn.example.com/logos/acme.png",
];
// or, with the name to display
document.querySelector("#logo").value = [
  { url: "https://cdn.example.com/logos/0c1d-acme.png", name: "acme.png" },
];

Filling its parent

The dropzone stretches to the height given to the field, set directly or by a flex or grid parent.

<div style="display: flex; flex-direction: column; height: 16rem">
  <eun-input-file label="Cover" style="flex: 1 1 auto"></eun-input-file>
</div>

Custom

Override the --input-file-* CSS variables, listed in full in the API tab.

<eun-input-file class="custom-input-file" label="Custom"></eun-input-file>
.custom-input-file {
  --input-file-border-color: #be185d;
  --input-file-border-color-hover: #be185d;
  --input-file-border-color-focus: #be185d;
  --input-file-focus-outline-color: #be185d;
}

The custom properties that change colors, borders, shadows and corners, at a glance. The API tab lists every property of the component.

Variable Sets
--input-file-border-color Sets the border color of the dropzone
--input-file-border-color-hover Sets the border color on hover
--input-file-border-color-focus Sets the border color on focus
--input-file-border-radius Sets the corner radius of the dropzone and items
--input-file-background Sets the background color of the dropzone
--input-file-background-hover Sets the background color on hover or drag over
--input-file-text-color Sets the text color
--input-file-icon-color Sets the color of icons and action buttons
--input-file-error-color Sets the color of the error message
--input-file-hint-color Sets the color of the hint message
--input-file-focus-outline-color Sets the color of the focus outline
--input-file-item-background Sets the background color of each item
--input-file-item-border-color Sets the border color of each item

Keyboard interactions

Key Action
Tab / Shift+Tab Moves focus in/out of the dropzone (or the first item, once a file already fills its place), then through each item and its own remove/checkbox buttons
Enter / Space on the dropzone Opens the native OS file picker, natively
Enter / Space on an item Opens that file in a new tab
Delete / Backspace on an item Removes that file directly, without needing to reach its own remove button
Enter / Space on a remove/checkbox/action button Activates that button, natively

Drag-and-drop is a pointer-only shortcut for the exact same selection the native picker already provides through the dropzone's Enter/Space, so nothing here is drag-and-drop-only.

Semantics and screen readers

Aria attributes

  • aria-invalid="true"|"false" on the native file field: true while the error is shown, or while invalid is set
  • aria-label on the native, visually-hidden file field: set from the label property directly, for the same cross-shadow-boundary reason documented on eun-input's own Accessibility tab (a plain-text aria-label instead of aria-labelledby/for pointing at the rendered <eun-label>)
  • aria-describedby="description": set on the native field whenever a hint or error message is actually rendered below it
  • aria-disabled on the dropzone: reflects both disabled and "max-files already reached"
  • Each item is role="button" with an aria-label combining the file name and a short "Open in a new tab" hint (open-label), so its accessible name and its primary action always match
  • Each item's remove button and (when selectable) checkbox carry their own aria-label including the file name, so multiple items' controls stay distinguishable to assistive technology
  • The file count above the items ("3 files") is aria-live="polite", so additions and removals are announced without moving focus

Label accessibility

Always set label (or slot content into the label slot): describe what the files are for ("Proof of address", "Product photos"), not just that a file is expected.

Field states

Disabled vs. readonly

disabled removes the field from the tab order (tabindex="-1", aria-disabled="true"), disables every action, and excludes the value from form submission entirely. readonly keeps the field and its items focusable and their values submitted, but blocks adding, removing, or reordering, communicating "you can't change this right now", not "this doesn't apply". Prefer readonly over disabled whenever the already-selected files are still meaningful context for the user.

Required field

required behaves like a native required <input type="file">: an empty selection is invalid, any non-empty selection is valid, regardless of multiple.

Rejections aren't validation errors

Files rejected for type/size/max-files never reach value in the first place, so there's nothing invalid to report through checkValidity(). A native file picker behaves the same way, since you simply can't pick a mismatched file. Listen for eunfilereject if you want to surface why a specific file didn't make it in (see the Examples tab).

Reference links

WAI Web Accessibility Tutorials: Labeling Controls
WHATWG HTML: The file upload state of the input element