MenuChevron Down
FileUpload - Docs - Artefact

FileUpload

Forms
Smart auto-detect

Introduction

A dropzone and file picker for selecting one or more files, with drag-and-drop, client-side validation (type/size/count), and a live preview list — all composed from Ark UI-equivalent parts on top of a native <input type="file"> so the form still works with JavaScript disabled.

Usage

Drag and drop or browse files
    import { FileUpload } from "../components/ui";
    
    export default function MyPage() {
      return (
        <FileUpload
          label="Upload Images"
          name="images"
          accept="image/*"
          maxFiles={2}
          dropzoneText="Drag and drop or browse files"
        />
      );
    }
    

    Custom Composition

    The default composition (label + dropzoneText + triggerText props) covers the common case. Pass explicit children for full control over layout — swap in any subset of the parts below:

    import { FileUpload } from "../components/ui";
    
    export default function MyPage() {
      return (
        <FileUpload name="attachments" maxFiles={5} accept="image/*,.pdf">
          <FileUpload.Label>Attachments</FileUpload.Label>
          <FileUpload.Dropzone>
            <span>Drop files here, or</span>
            <FileUpload.Trigger>browse</FileUpload.Trigger>
          </FileUpload.Dropzone>
          <FileUpload.List showSize clearable />
          <FileUpload.ClearTrigger>Clear all</FileUpload.ClearTrigger>
          <FileUpload.HiddenInput />
        </FileUpload>
      );
    }
    

    CMS Page Builder

    This component is available as a file-upload (alias fileUpload) block in the Page Builder (content/pages/*.json):

    {
      "type": "file-upload",
      "label": "Resume",
      "name": "resume",
      "accept": ".pdf,.docx",
      "maxFiles": 1,
      "maxFileSize": 5242880,
      "dropzoneText": "Drag your file(s) here",
      "triggerText": "Open file picker",
      "showSize": true,
      "clearable": true
    }
    

    maxFiles and maxFileSize are coerced from strings to numbers by the renderer; every other field passes straight through to FileUpload. It always renders interactive in the Page Builder.

    Props

    Root

    PropTypeDescription
    acceptstring | string[] | Record<string, string[]>Accepted file types — a MIME string, a list, or a MIME→extensions record. Normalised to the native accept attribute.
    allowDropbooleanWhether to allow drag-and-drop in the dropzone. Default true.
    capture"user" | "environment"The default camera to use when capturing media.
    directorybooleanWhether to accept directories (webkitdirectory).
    disabledbooleanDisables the dropzone, trigger, and hidden input.
    invalidbooleanMarks the field invalid (data-invalid on every part).
    requiredbooleanMarks the underlying input required.
    maxFilesnumberMaximum number of files. Default 1. Selecting > 1 switches the island from replace to append mode.
    maxFileSizenumberMaximum file size in bytes. Default Infinity.
    minFileSizenumberMinimum file size in bytes. Default 0.
    namestringName of the underlying file input, for native form submission.
    localestringBCP-47 locale used for file-size formatting. Default "en-US".
    translationsPartial<FileUploadTranslations>Overrides for the dropzone/preview/delete/clear ARIA strings.
    size"sm" | "md" | "lg"Visual size variant. Default "md".
    acceptedFilesFile[]Controlled list of accepted files (island only).
    defaultAcceptedFilesFile[]Initial list of accepted files, uncontrolled (island only).
    preventDocumentDropbooleanStops the browser from navigating to a file dropped outside the dropzone. Default true (island only).
    validate(file: File, details: FileValidateDetails) => FileError[] | nullCustom validation, run after the built-in type/size/count checks (island only).
    transformFiles(files: File[]) => Promise<File[]>Transforms newly accepted files (e.g. compress) before they're committed (island only).
    onFileAccept(details: FileAcceptDetails) => voidCalled with the files accepted from the most recent selection or drop.
    onFileReject(details: FileRejectDetails) => voidCalled with any rejected files and their error codes.
    onFileChange(details: FileChangeDetails) => voidCalled after every selection with the full accepted/rejected sets.
    interactivebooleanForces (or suppresses) hydration as an island.

    Default composition

    These props only apply when children is omitted — FileUpload then renders Label + Dropzone + Trigger + List + HiddenInput for you.

    PropTypeDescription
    labelstringLabel rendered above the dropzone.
    dropzoneTextstringDropzone helper text. Default "Drag your file(s) here".
    triggerTextstringTrigger button text. Default "Open file picker".
    showSizebooleanShow each file's formatted size in the list. Default true.
    clearablebooleanShow a per-file delete trigger in the list. Default true.

    Sub-components

    FileUpload is a compound component — every part below reads from the FileUpload.Root context, so they only work nested inside it (or inside a FileUpload.Item for the item-scoped parts).

    PartDescription
    FileUpload.Label<label> bound to the hidden input via for.
    FileUpload.DropzoneDrop target; renders role="button" with keyboard support (Enter/Space opens the picker).
    FileUpload.TriggerOpens the file picker. Renders as a <label for> (not a <button>) so it keeps working without JavaScript.
    FileUpload.HiddenInputThe visually-hidden native <input type="file"> that actually holds the selection for form submission.
    FileUpload.ItemGroup<ul> wrapping the accepted-file Items.
    FileUpload.ItemOne accepted file. Requires a file prop; provides file context to its children.
    FileUpload.ItemNameThe file's name, or children to override.
    FileUpload.ItemSizeTextThe file's formatted size (via formatBytes), or children to override.
    FileUpload.ItemPreviewWrapper shown only when the file's MIME type matches its type regex prop (default ".*").
    FileUpload.ItemPreviewImageClient-only <img> preview via URL.createObjectURL; renders nothing during SSR or for non-image files.
    FileUpload.ItemDeleteTriggerRemoves this item from the accepted-files list.
    FileUpload.ClearTriggerClears every accepted file. Hidden automatically when the list is empty.
    FileUpload.ItemsComposite: maps accepted files to Items with an image/file-icon preview, name, optional size, and optional delete trigger. Takes showSize / clearable / files.
    FileUpload.ListComposite: ItemGroup wrapping Items. Same showSize / clearable / files props.
    FileUpload.FileTextShows the first selected file's name, an "N files" count for multi-select, or a fallback string when nothing is selected.
    import { FileUpload, formatBytes } from "../components/ui";
    

    formatBytes(bytes, locale?) and the FileAccept / FileError / FileRejection / FileChangeDetails / FileUploadTranslations types are also exported for consumers building a fully custom composition.

    Validation

    Each selected file is checked in order and rejected on the first failing rule; validate runs last, after every built-in check passes:

    FileError codeTrigger
    FILE_INVALID_TYPEDoesn't match accept.
    FILE_TOO_LARGEfile.size > maxFileSize.
    FILE_TOO_SMALLfile.size < minFileSize.
    FILE_EXISTSSame name/size/type already accepted (multi-file mode only).
    TOO_MANY_FILESAccepting this file would exceed maxFiles.
    FILE_INVALIDReserved for custom validate results.

    With maxFiles={1} (the default), a new accepted file replaces the current selection. With maxFiles > 1, new files are appended to the existing selection up to the limit.

    Accessibility

    • Dropzone renders role="button" with aria-label from translations.dropzone, aria-disabled when disabled, and is keyboard operable (tabIndex={0}, Enter/Space opens the file picker).
    • Label and Trigger are real <label for> elements bound to the hidden input's id, so clicking either opens the native file picker even before hydration.
    • ItemDeleteTrigger and ClearTrigger get aria-labels from translations.deleteFile(file) / translations.clearFiles, overridable via the translations prop.
    • data-disabled / data-invalid / data-required / data-readonly / data-dragging are mirrored onto every part for styling and assistive-tech state.