PixelFileUpload

Dropzone + click-to-browse file uploader with accept/size/count validation, image thumbnails, and per-item removal.

  • Drag-and-drop or click/keyboard to open the native file picker
  • Validates against accept, maxSize, and maxFiles and reports the rejected files (onReject, @reject in Vue, (reject) in Angular)
  • Image previews via object URLs with automatic revoke on unmount
  • Controlled file list — value + onChange (React), v-model (Vue), [(value)] or forms (Angular) — or uncontrolled with defaultValue
  • Surface-aware styling with size, label, hint, and error props
Status
stable
Category
forms
Since
v1.8.0

API

import { PixelFileUpload } from '@pxlkit/ui-kit';
PropTypeDefaultDescription
valueFile[]—Files; leave unset for an uncontrolled field.
defaultValueFile[]—Initial files while uncontrolled.
onChange(files: File[]) => void—Called with the new files, after each choice, drop or removal.
acceptstring—Types the field takes: MIME types, type/* wildcards and .ext extensions, comma-separated.
multiplebooleanfalseFiles add up; without it each choice replaces the last.
maxSizenumber—Bytes per file.
maxFilesnumber—Most files the field holds.
dropzonebooleantrueShows the dropzone; false shows a browse button instead.
renderItem(file: File, remove: () => void) => React.ReactNode—Draws a file's row in place of the default one: gets the file, and a function that removes it.
onReject(rejections: PixelFileRejection[]) => void—Called with the files turned down by a choice or a drop, with their reasons.
surface'pixel' | 'linear'—Surface override; defaults to the nearest provider.
size'sm' | 'md' | 'lg''md'Padding and type size of the dropzone.
labelstring—Label above the field, pointing at the file input.
hintstring—Helper text below the field; hidden while error is set.
errorstring—Error message below the field; turns the dropzone red.
disabledboolean—Disables choosing, dropping and removing files.
namestring—Deprecated form-serialization hint. Files are not serializable through a hidden mirror once e.target.value is reset, so this prop no longer wires to a native input. Read selected files from onChange and POST them manually (e.g. FormData.append(name, file) per file). Kept in the prop bag so consumers using it don't break — the id of the file input still uses it via the id prop fallback path.
idstring—id of the file input; generated when left out.
classNamestring—Extra classes on the root element.

ref points to <div>.

Accessibility

WCAG target: 2.1 AA

  • button-like dropzone
  • aria-describedby for accepted formats

Dropzone exposes role="button" with tabIndex 0 (or -1 when disabled) and, while a hint or error shows, aria-describedby pointing at it. The hidden <input type="file"> is aria-hidden while the dropzone is active. Without the dropzone the input is the one control and tab stop: the browse button is a second <label> of it, so the input is named by both labels, carries the aria-describedby, and the button shows its keyboard focus.

Keyboard

KeyActionWhen
EnterOpens the native file pickerdropzone has focus
SpaceOpens the native file pickerdropzone has focus
Enter / SpaceOpens the native file pickerfile input has focus (no dropzone)

Usage

import { useState } from 'react';
import { PixelFileUpload } from '@pxlkit/ui-kit';

export function Default() {
  const [files, setFiles] = useState<File[]>([]);
  return (
    <PixelFileUpload
      label="Upload files"
      hint="PNG or JPG, up to 5 MB each"
      value={files}
      onChange={setFiles}
      accept="image/*"
      multiple
      maxSize={5 * 1024 * 1024}
      maxFiles={5}
    />
  );
}

Examples

Default

import { useState } from 'react';
import { PixelFileUpload } from '@pxlkit/ui-kit';

export function Default() {
  const [files, setFiles] = useState<File[]>([]);
  return (
    <PixelFileUpload
      label="Upload files"
      hint="PNG or JPG, up to 5 MB each"
      value={files}
      onChange={setFiles}
      accept="image/*"
      multiple
      maxSize={5 * 1024 * 1024}
      maxFiles={5}
    />
  );
}

Button mode

import { useState } from 'react';
import { PixelFileUpload } from '@pxlkit/ui-kit';

export function ButtonMode() {
  const [files, setFiles] = useState<File[]>([]);
  return (
    <PixelFileUpload
      label="Choose a file"
      value={files}
      onChange={setFiles}
      dropzone={false}
    />
  );
}

With error

import { PixelFileUpload } from '@pxlkit/ui-kit';

export function WithError() {
  return (
    <PixelFileUpload
      label="Attachments"
      error="At least one file is required"
      accept=".pdf,.doc,.docx"
    />
  );
}

Related