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
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-reactimport { Dropzone } from "@wizeworks/silicaui-react";Props
| Prop | Type | Required | Description |
|---|---|---|---|
| onFiles | (files: File[]) => void | no | Called with the accepted files (from a drop or the picker). |
| onReject | (rejections: DropzoneRejection[]) => void | no | Called with files rejected by accept / maxSize. |
| accept | string | no | Native-style accept list, e.g. "image/*,.pdf". |
| multiple | boolean | no | Allow selecting/dropping more than one file. Default true. |
| maxSize | number | no | Max size per file, in bytes. |
| disabled | boolean | no | — |
| title | React.ReactNode | no | Primary line. Default "Drop files here, or click to browse". |
| hint | React.ReactNode | no | Secondary hint line (e.g. accepted types). |
| icon | React.ReactNode | no | Override the default upload icon. |
| inputProps | React.InputHTMLAttributes<HTMLInputElement> | no | Extra 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-behaviorsimport { 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.