Dropzone
Files, dropped or chosen. The area is only its corner marks, and files carried over it close them in; the sentence is a real file input, and what you hold is listed as attachments.
Report an issue with DropzoneExample
Rest
Hover
Focus
Files over it
ghost, holding two
ghost, files over it
Disabled
Install
Run it in a project that has the shadcn CLI set up. It brings the 0nlyType base and attachment along.
npx shadcn@latest add https:// thedirectors.agency/ ui/ r/dropzone.jsonUsage
"use client"import * as React from "react"import { Dropzone } from "@/components/ui/dropzone"// ponytail: stand-in files of the right size, made once, so the docs can show what's held.const file = (name: string, bytes: number, type: string) => new File([new Uint8Array(bytes)], name, { type, lastModified: 0 })const useHeld = () => React.useMemo(() => [file("Halden brief, second round.pdf", 2_400_000, "application/pdf"), file("Stamp sizes.png", 3_600_000, "image/png")], [])export default function Example() {return (<div className="grid w-full max-w-[40rem] gap-(--ot-space-8)"><Dropzone accept=".pdf,.png,.svg" maxSize={10e6} name="marks" hint="PDF, PNG or SVG, up to 10 MB each." /><Dropzone variant="ghost" prompt="Drop the second round here" hint="Anything you've marked up. It stays until you send it." defaultFiles={useHeld()} /></div>)}Contract
- Underneath: a native file input, plus a hook.
- Anatomy:
<div class="ot-drop" data-variant="corners | ghost">holding<label class="ot-drop-zone ot-corners">(the file input as.ot-sr, for ghost.ot-drop-ghost(aria-hidden),.ot-drop-wordsending in.ot-link"choose", then an optional.ot-drop-hintthat describes the input), then anot-attachmentslist of what's held.data-overwhile files are carried over it,data-disabled. - An area defined only by its corner marks (
ot-corners,--ot-cornerat--ot-space-4), after "the silence that heals": a frame that doesn't close. The sentence is the control: "Drop files here, or choose". The whole area is the input's label, so a click anywhere opens the picker; Tab reaches the real input, and focus draws the highlighter through "choose" (the link's own look) and sets the corners in the accent. - Pointing sketches the corners in pencil. Carry files over it and the corners close in by
--ot-space-4and ink, landing with spiccato, and the words ink; let go or leave and they spring back out. The corners' colour is a registered--ot-drop-c, so it crosses over rather than jumps. - What's held is listed as attachment rows, each waiting (
idle, the hairline ring of a file still to go) with its size and a bracket Remove. Each drop or choice adds to them, and the same file isn't held twice. A refused file is a crimson row that says what to fix: "This takes PDF, PNG or SVG. Choose another file." or "Choose one under 10 MB; this is 14.2 MB." The real input always holds exactly what's listed (through aDataTransfer), so a form sends what you see. Resetting the form clears the files and refused rows, and callsonFilesChangewith no files, as removing the last file does.list={false}leaves the listing to you, for rows with upload progress. - Ghost: after the huge ghost numerals behind "the silence that heals". The number of files you carry stands huge and faint behind the words (
--ot-fffin the expression italic, ink at 8%, 14% while over), set against the area's foot and end and cropped by them, two figures as the poster's 14 and 08. Dropped, it stays as the number held. It turns over as the count moves, up as files come and down as they go. - States: rest, hover, focus, files over it, holding, refused (the row), disabled (the corners at the rule, the words in pencil). Forced colours: the corners are
GrayText,Highlighton focus,CanvasTextwhile over; the ghost is dropped. - Keyboard: Tab to the input; Enter or Space opens the picker (native). Remove is a button.
Props
| Prop | Type | Default | What it does |
|---|---|---|---|
variant | "corners" | "ghost" | "corners" | corners: the corner marks of "the silence that heals" frame the area; pointing sketches them in pencil, files carried over close them in and ink them, focus sets them in the accent. ghost: the same, and the number of files you carry stands huge and faint behind the words, after the poster's ghost numerals; once dropped it stays as the number held, turning over as it changes. |
accept | string | The file input's accept, such as .pdf,.png or image/*. A file that doesn't match is listed in crimson and says what the area takes. | |
maxSize | number | The largest file, in bytes. A larger one is refused with its size and the limit. | |
multiple | boolean | true | Take several files. Each drop or choice adds to what's held; the same file isn't held twice. |
name | string | Submits the files with a form. The real input always holds exactly what's listed, removals included. | |
prompt | string | "Drop files here" | The start of the sentence; ", or choose" follows it. |
hint | ReactNode | What it takes, in pencil under the sentence. Describes the input. | |
defaultFiles | File[] | Files held from the start, such as a saved draft's. | |
onFilesChange | (files: File[]) => void | Called with every file held after each drop, choice or removal. | |
list | boolean | true | List what's held as attachment rows, each with Remove. Turn it off to list them yourself, with their upload progress. |
disabled | boolean | false | The corners and words go pencil and nothing is taken. |
Craft
Where its move comes from
| Component | Reference | Move |
|---|---|---|
| Dropzone | "the silence that heals" corners | The area is only its corner marks; files carried over close them in |
| Ghost dropzone | "the silence that heals" ghost numerals | The number of files carried stands huge and faint behind the words |
How it moves
| Component | Articulation | What moves |
|---|---|---|
| Dropzone | Close in | Files carried over close the corners in and ink them, landing with spiccato |
| Ghost dropzone | Count | The ghost number turns over as files come and go |