Pass `sources` to render the Uppy Dashboard with an import-from row. Device and Screencast work with no Companion; Url, Dropbox, and Google Drive route through a Companion server at `companionUrl`.
npx shadcn@latest add https://sdk-components.thesqd.com/r/file-upload-v2.jsonnpm install @uppy/core @uppy/react @uppy/aws-s3 \
@uppy/xhr-upload @uppy/thumbnail-generatorimport { FileUploadV2 } from "@/components/blocks/file-upload-v2";"use client";
import { FileUploadV2 } from "@/components/blocks/file-upload-v2";
// Single component, every "variant" driven by props:
// label / caption / error / restrictionText / showUploadedUrls
// acceptedFileTypes / maxFileSize / maxFiles / maxTotalFileSize / allowMultiple
// imageValidateSizeMin/MaxWidth/Height
// destination ("s3" | "dropbox") / dropboxFolder
export function Example() {
return (
<FileUploadV2
label="Files"
allowMultiple
maxFiles={2}
maxFileSize="10MB"
acceptedFileTypes={["image/*"]}
restrictionText="Images only"
showUploadedUrls
/>
);
}What `shadcn add file-upload-v2.json` drops in for you:
- src/components/blocks/file-upload-v2.tsx (the Uppy component)
- src/components/ui/{alert,label,field-hint}.tsx (registry deps)
- src/app/api/s3-presign/route.ts (S3 presign proxy)
- src/app/api/dropbox-upload/route.ts (Dropbox destination proxy)
- .env.example (the env vars the routes read)
- npm: @uppy/core, @uppy/react, @uppy/aws-s3,
@uppy/xhr-upload, @uppy/thumbnail-generator, lucide-react
No global CSS wiring needed — the Uppy block renders its
own item cards with Tailwind, so there's no stylesheet to import.
What you still have to wire up by hand:
1. Copy `.env.example` → `.env.local` and set `SQUAD_API_KEY`. Both the S3
presign route and the Dropbox route read it. Without it, uploads 500.
2. Brand-new project? Install the theme first (tokens, `cn`, shared deps):
npx shadcn@latest add https://sdk-components.thesqd.com/r/theme.jsonFileUploadV2 (Uppy engine — one flat component)
├── @uppy/core (reactive state: files, progress, restrictions)
├── @uppy/aws-s3 (destination="s3": presign → PUT to Wasabi)
├── @uppy/xhr-upload (destination="dropbox": POST → /api/dropbox-upload)
├── @uppy/thumbnail-generator (image previews when allowImagePreview)
└── restrictions (maxFileSize / maxNumberOfFiles / maxTotalFileSize / allowedFileTypes)
Item cards render from useUppyState — no DOM scraping for progress. Image
dimension bounds are checked in a file-added handler (Uppy has no built-in
dimension restriction). Pair with the relevant route handler:
• S3: src/app/api/s3-presign/route.ts → api.thesqd.com /v1/s3/get-presigned-upload-url
• Dropbox: src/app/api/dropbox-upload/route.ts → api.thesqd.com /v1/dropbox/upload-filesPass `sources` to render the Uppy Dashboard with an import-from row. Device and Screencast work with no Companion; Url, Dropbox, and Google Drive route through a Companion server at `companionUrl`.
Wrapper — label / hint / error
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Field label rendered above the uploader. |
caption | ReactNode | — | Helper copy shown directly under the label via FieldLabel. |
error | boolean | string | — | Static error state. `true` applies the red panel only; a string also renders the inline message (`text-xs mt-1.5 text-red-500`) below the uploader. Overridden by runtime validation messages. |
showNetworkStatus | boolean | false | Render a live network-quality + estimated-time-left readout under the uploader while files are in flight (e.g. "Network good · 42.5% · ~1m 20s left"). Upload rate is sampled over a fixed window and EMA-smoothed; a stall reads as "Reconnecting…" and going offline shows "Offline — will resume". |
progressDecimals | number | 0 | Decimal places for the overall percent in the network readout (`0` → "42%", `1` → "42.5%"). Only applies with `showNetworkStatus`. |
onwarning | (err, file?, status?) => void | — | Validation warning callback (e.g. file count exceeded). (v1 engine) Displays `err.body ?? err.main` as an amber-600 message below the uploader. Clears on a successful `onaddfile`. |
onerror | (err, file?, status?) => void | — | Upload error callback (e.g. server upload failure). (v1 engine) Displays `err.body ?? err.main` as a red-500 message below the uploader. Clears on a successful `onaddfile`. |
Core
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | — | Form field name applied to the upload request body. |
allowMultiple | boolean | false | Accept more than one file in a single picker. |
instantUpload | boolean | true | Upload as soon as the user adds a file. |
server | FileUploadServerConfig["server"] | — | Custom upload pipeline. Most apps implement `process` and pass the response body to `load()` so it lands as the file's serverId. |
credits | boolean | true | Show the uploader branding in the corner. (v1 only.) |
destination | "s3" | "dropbox" | "s3" | v2 (Uppy) only. Where uploads land: `s3` presigns + PUTs to Wasabi; `dropbox` POSTs each file to the Squad API Dropbox endpoint via `/api/dropbox-upload`. Read once at mount. |
dropboxFolder | string | "/Squad SDK Uploads" | v2 (Uppy) only. Dropbox destination folder when `destination="dropbox"`; the file name is appended. |
sources | ("device" | "url" | "screenCapture" | "googleDrive" | "googleDrivePicker" | "dropbox")[] | — | v2 (Uppy) only. Import sources. Non-empty renders the Uppy Dashboard with an import-from row. `device`/`screenCapture` need no server; `url`/`dropbox`/`googleDrive` route through `companionUrl` (server OAuth, works on any host). `googleDrivePicker` is the client-side Picker — slicker, but Google requires a public-TLD origin so it can't run on `*.localhost` dev hosts. |
companionUrl | string | — | v2 (Uppy) only. Base URL of your `@uppy/companion` server. Required for the `url`, `dropbox`, and `googleDrive` sources. |
googleClientId / googleApiKey / googleAppId | string | — | v2 (Uppy) only. Google Cloud OAuth client ID / API key / project number — only for the client-side `googleDrivePicker` source. The Companion-backed `googleDrive` source needs none of these. |
Validation
| Prop | Type | Default | Description |
|---|---|---|---|
maxFiles | number | — | Cap how many files can be selected. |
maxFileSize | string | — | Per-file size limit, e.g. "10MB". |
maxTotalFileSize | string | — | Combined size cap across every selected file. |
acceptedFileTypes | string[] | — | Whitelist of MIME types, e.g. ["image/*", "application/pdf"]. |
allowImagePreview | boolean | — | Render image thumbnails for image files and lay items out in a 2-column grid. Defaults to true; set false to stack items full-width. |
imageValidateSizeMinWidth / MinHeight / MaxWidth / MaxHeight | number | — | Image dimension bounds (min/max width & height). |