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?