Dropzone

Forms

Drag-and-drop uploader with pixel borders, helper messaging, and removable previews.

Import

import { PixelDropzone } from "@/components/ui/pixel/pixel-dropzone"

Usage

Upload Assets

PNG, GIF or WEBP up to 10MB

Component Source

Copy and paste the following code into your project at /src/components/ui/pixel/pixel-dropzone.tsx

"use client";

import { FileText, Image as ImageIcon, UploadCloud, X } from "lucide-react";
import * as React from "react";
import { cn } from "@/lib/utils";

type FileRenderer = (file: File, index: number) => React.ReactNode;

type PixelDropzoneProps = {
  label?: React.ReactNode;
  description?: React.ReactNode;
  helperText?: React.ReactNode;
  hasError?: boolean;
  accept?: string | string[];
  maxFiles?: number;
  multiple?: boolean;
  disabled?: boolean;
  files?: File[];
  defaultFiles?: File[];
  renderFile?: FileRenderer;
  onFilesChange?: (files: File[]) => void;
  onFileRemove?: (file: File, index: number) => void;
  inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
  className?: string;
  dropzoneClassName?: string;
  helperClassName?: string;
};

function normalizeAccept(accept?: string | string[]) {
  if (!accept) return undefined;
  const list = Array.isArray(accept) ? accept : accept.split(",");
  return list.map((entry) => entry.trim()).filter(Boolean);
}

function matchesAccept(file: File, acceptList?: string[]) {
  if (!acceptList?.length) return true;

  return acceptList.some((rule) => {
    if (rule.startsWith(".")) {
      return file.name.toLowerCase().endsWith(rule.toLowerCase());
    }

    if (rule.endsWith("/*")) {
      const prefix = rule.slice(0, -1);
      return file.type.startsWith(prefix.slice(0, -1));
    }

    return file.type === rule;
  });
}

// ... (more code below)

Props

PropTypeDefaultDescription
acceptstring | string[]-Accepted MIME types or file extensions.
maxFilesnumber-Maximum number of files that can be queued.
multiplebooleantrueAllow selecting multiple files at once.
filesFile[]-Controlled list of files for preview rendering.
onFilesChange(files: File[]) => void-Callback fired whenever the selection changes.
helperTextReact.ReactNode-Helper or status text displayed below the dropzone.
hasErrorbooleanfalseTints the helper text and border for error states.

Examples

Basic Image Upload

Upload Assets

PNG, GIF or WEBP up to 10MB

Custom Preview

Upload Assets

PNG, GIF or WEBP up to 10MB

Accessibility

This component is built with accessibility in mind, including proper ARIA attributes, keyboard navigation, and focus management.