Form File Uploads
Forms can collect files — resumes, invoices, photos, documents — and persist them to your own storage instead of a third-party CDN. Attachment fields render a file picker (and optional drag-and-drop zone), validate type and size before upload, show progress and previews, and submit a canonical { url, name, size, mimeType } metadata object. Files land in a configured app.buckets[] destination on your S3, local disk, or database storage.
buckets:
- name: applications
maxFileSize: 10485760
forms:
- id: 1
name: apply
title: Job Application
path: /apply
submitTo: { table: applications }
fields:
- { kind: standalone, name: full_name, inputType: single-line-text, required: true }
- kind: standalone
name: resume
inputType: attachment
label: Resume
required: true
accept: 'application/pdf,.doc,.docx'
maxFileSize: 10485760 # 10 MB
dropZone: trueAttachment Field Configuration
Attachment behavior is configured the same way on both field kinds: a kind: standalone field with inputType: attachment, or a kind: table-field whose underlying column is single-attachment / multiple-attachments. The upload props are shared.
| Property | Description |
|---|---|
accept |
Comma-separated MIME types or extensions restricting the file dialog (e.g. image/*, application/pdf, .doc,.docx). |
maxFileSize |
Maximum size in bytes per file. Larger files are rejected with an inline error before upload starts. |
maxFiles |
Maximum number of files (multiple-file fields only). Selecting more surfaces an inline error. |
dropZone |
Render a drag-and-drop area alongside the picker, accepting the same files. |
Single vs multiple. A single-file attachment submits one FileMetadata object (or null when empty); a multiple-file attachment submits a FileMetadata[] array (or []). For table-bound fields the multiplicity is inferred from the column type (single-attachment vs multiple-attachments); for standalone fields it follows the upload UI presented by inputType: attachment and maxFiles.
Bucket Binding
The storage destination comes from app.buckets[]. A form inherits a default bucket: the bucket whose default: true flag is set, or the only bucket when exactly one is defined. Files are uploaded there on submit, and the form payload carries canonical metadata for each file. Per-field bucket overrides are reserved for a follow-up release.
buckets:
- name: documents
default: true
backend: local
config: { path: ./storage/documents }
forms:
- id: 2
name: invoice-upload
title: Upload Invoice
submitTo: { automation: process-invoice }
fields:
- { kind: standalone, name: vendor, inputType: single-line-text, required: true }
- kind: standalone
name: document
inputType: attachment
accept: 'application/pdf,image/*'
maxFileSize: 20971520 # 20 MB
dropZone: trueUpload UX
The attachment field renders a complete upload experience:
| Behavior | Description |
|---|---|
| Type filter | accept restricts both the browser dialog and drag-and-drop; mismatched files surface an inline error. |
| Size validation | maxFileSize rejects oversized files before upload begins. |
| Progress indicator | A progress bar or spinner is shown while each file uploads. |
| Preview | Image files render a thumbnail after upload; non-image files render filename + size. |
| Remove | Selected files can be removed before submit (keyboard-accessible); removing one of many does not affect the others. |
| Required enforcement | A required attachment field with no file blocks submission with an inline error. |
File Metadata Shape
When the form submits, each attachment value is normalized to a canonical metadata object — the same shape consumed by submitTo.table, submitTo.automation triggers, and the form_submissions ledger data payload.
type FileMetadata = {
url: string // canonical URL into the bucket (signed when the bucket is private)
name: string // original filename
size: number // bytes
mimeType: string // detected MIME type
}A single-file field submits FileMetadata | null; a multiple-file field submits FileMetadata[]. Routing the upload to an automation makes the same metadata available in the trigger payload — for example to run an AI extraction over {{trigger.data.document.url}}.
Related Pages
- Form Fields — the field kinds attachment uploads extend.
- Forms Overview —
submitTotargets that receive file metadata. - Submissions — file metadata stored in the ledger
datapayload. - Buckets — configuring S3, local, and database storage backends.
Last updated August 11, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.