Dropzone
A single, app-wide target for files dragged anywhere in the document. It works in any React framework; consumers provide the storage and state adapter they need.
Installation
import { Dropzone } from "@o/pipeline/experimental/components/dropzone";
Usage
Mount exactly one Dropzone beneath your app’s Pipeline Root or Provider, never once per route or chat. It detects and validates files only; it never stores or uploads them. While a file is being dragged, the status layer portals to the current document’s body, covering the browser viewport even when the application is transformed or clipped. Keep a separate keyboard- and touch-accessible file picker for people who cannot drag files.
import { useSetAtom } from "jotai";
import { Dropzone } from "@o/pipeline/experimental/components/dropzone";
import { addChatFilesAtom } from "./chat-files";
export function AppDropzone() {
const addChatFiles = useSetAtom(addChatFilesAtom);
return (
<Dropzone
label="Drop any file here to add to the chat"
onDrop={(acceptedFiles, fileRejections) => {
addChatFiles({ acceptedFiles, fileRejections });
}}
/>
);
}
import { atom } from "jotai";
import type { FileRejection } from "react-dropzone";
type ChatFiles = {
acceptedFiles: readonly File[];
fileRejections: readonly FileRejection[];
};
export const chatFilesAtom = atom<ChatFiles>({
acceptedFiles: [],
fileRejections: [],
});
export const addChatFilesAtom = atom(null, (_get, set, files: ChatFiles) =>
set(chatFilesAtom, files),
);
Do not use atomWithStorage for raw File objects. Persist file metadata or uploaded-file identifiers instead.
Demo
New chat
Can you review the launch plan and tell me what needs attention first?