Dropzone

A drag-and-drop file dropzone with keyboard and click fallbacks, accessible status messaging, and tokened states.

Real use · image upload with rejection reasons

Drop files here, or click to browsePNG or JPG, up to 2MB

Using Dropzone

Silica ships one design system through three paths. They share a class vocabulary but are not interchangeable — pick the one that matches what you are building.

Plain HTML — CSS classes

The Tailwind v4 plugin. Works in any framework or none — Rails, Django, PHP, Go, a static file. This path ships no JavaScript, so Dropzone is styled but inert: its behavior comes from dropzone. For that behavior with no framework, use the node-tree path below.

/* in your CSS */
@import "tailwindcss";
@plugin "@wizeworks/silicaui";

Root class dropzone, with 4 modifiers:

  • .dropzone
  • .dropzone-hint
  • .dropzone-icon
  • .dropzone-input
  • .dropzone-title

React

Typed components over those same classes, with behavior and accessibility from Base UI.

npm i @wizeworks/silicaui-react
import { Dropzone } from "@wizeworks/silicaui-react";

Props

PropTypeRequiredDescription
onFiles(files: File[]) => voidnoCalled with the accepted files (from a drop or the picker).
onReject(rejections: DropzoneRejection[]) => voidnoCalled with files rejected by accept / maxSize.
acceptstringnoNative-style accept list, e.g. "image/*,.pdf".
multiplebooleannoAllow selecting/dropping more than one file. Default true.
maxSizenumbernoMax size per file, in bytes.
disabledbooleanno—
titleReact.ReactNodenoPrimary line. Default "Drop files here, or click to browse".
hintReact.ReactNodenoSecondary hint line (e.g. accepted types).
iconReact.ReactNodenoOverride the default upload icon.
inputPropsReact.InputHTMLAttributes<HTMLInputElement>noExtra props for the underlying <input type=file>.

Source of the demo above

The exact file that renders the examples above, so it can never drift from them. It imports a few of this site’s own layout helpers (../lib/…), so read it for the component calls rather than copying it whole.

Show source
import { useState } from "react";
import { Dropzone } from "@wizeworks/silicaui-react";
import { Section } from "../lib/Section";

export function DropzoneDemo() {
    const [files, setFiles] = useState<string[]>([]);
    const [rejected, setRejected] = useState<string[]>([]);

    return (
        <Section title="Real use · image upload with rejection reasons">
            <div className="flex max-w-md flex-col gap-3">
                <Dropzone
                    accept="image/*"
                    maxSize={2 * 1024 * 1024}
                    hint="PNG or JPG, up to 2MB"
                    onFiles={(fs) => setFiles(fs.map((f) => f.name))}
                    onReject={(rs) =>
                        setRejected(rs.map((r) => `${r.file.name} (${r.reason})`))
                    }
                />
                {files.length > 0 && (
                    <p className="text-xs text-success">Accepted: {files.join(", ")}</p>
                )}
                {rejected.length > 0 && (
                    <p className="text-xs text-error">Rejected: {rejected.join(", ")}</p>
                )}
            </div>
        </Section>
    );
}

Vanilla — node tree to HTML

dropzone — Dropzone is bare (presentational only, host owns the file list via onFiles-equivalent listening for sui:file); FileUpload adds a managed list part the behavior renders thumbnail/name/remove rows into directly (the one place a behavior owns real create-your-own-markup state, same precedent as Carousel's track/dots). Build a node tree, project it with toHtml(), and let the zero-dependency runtime hydrate it. This is the path for generated or user-authored documents — a site builder, CMS output, a static export.

npm i @wizeworks/silicaui-html @wizeworks/silicaui-behaviors
import { atom, toHtml } from "@wizeworks/silicaui-html";

const node = atom("Dropzone", "dropzone", { /* props */ }, [/* children */]);
const markup = toHtml(node);

Styling on this path is the class string in the second argument — the same classes as the Plain HTML section above. There is no color prop here; write "dropzone dropzone-primary" rather than passing a colour as a prop.

Takes children
yes
Hydrated by
dropzone

Load @wizeworks/silicaui-behaviors on the page or this markup renders correctly and does nothing.

The full API for Dropzone, on every path, is available through the Silica MCP server.