Skip to content

File Uploads

Build a file upload form with preview, validation, and server-side handling using the File Upload widget.

Database Table

Create a documents table:

ColumnType
idint
titlevarchar
descriptiontext
file_urlvarchar
file_namevarchar
file_sizeint
uploaded_byint
created_atdatetime

UI Layout

Form Container
  ├── Heading ("Upload Document")
  ├── Text Input (name: "title", label: "Document Title", required: true)
  ├── Textarea (name: "description", label: "Description")
  ├── File Upload (name: "file", label: "Choose File",
  │     accept: ".pdf,.doc,.docx,.xls,.xlsx",
  │     maxSize: 5242880)
  └── Submit Button ("Upload")

File Upload Widget Configuration

PropertyValueDescription
namefileThe form field name.
accept.pdf,.doc,.docx,.xls,.xlsxAllowed file types.
maxSize5242880Max file size in bytes (5 MB).

The File Upload widget handles:

  1. File selection via a browse button.
  2. Client-side file type and size validation.
  3. File preview (image files show a thumbnail; other files show the filename).
  4. Upload to the WordPress media library via the REST API.
  5. Returns the attachment URL and metadata to the form.

Action Pipeline (onSubmit)

Context
  → Require Auth (logged_in)
    → Rate Limit (maxRequests: 10, windowSeconds: 60, scope: "user")
      → Validate
        → Sanitize
          → Set Field (uploaded_by = current user id)
            → Set Field (created_at = now())
              → Save Row (tableId: documents)
                → Output

Validate Rules

json
[
  { "field": "title", "rule": "required" },
  { "field": "title", "rule": "max_length", "value": "255" },
  { "field": "file_url", "rule": "required" }
]

Sanitize Fields

json
[
  { "field": "title", "method": "strip_tags" },
  { "field": "title", "method": "trim" },
  { "field": "description", "method": "esc_html" },
  { "field": "description", "method": "trim" },
  { "field": "file_url", "method": "trim" },
  { "field": "file_name", "method": "strip_tags" },
  { "field": "file_size", "method": "to_int" }
]

Set Fields

uploaded_by:

fieldName: "uploaded_by"
expression: "user_meta('ID')"

created_at:

fieldName: "created_at"
expression: "now()"

Displaying Uploaded Documents

Data Source Pipeline

Table Query (documents, orderBy: "created_at", order: "DESC")
  → Result

Display Layout

Repeater (documents data source)
  └── Container (flex, row, gap: 12px)
      ├── Text ("`{ {row.title}}`")
      ├── Text ("`{ {number_format(row.file_size / 1024, 0)}}` KB")
      ├── Text ("`{ {date_format(row.created_at, 'M j, Y')}}`")
      └── Button ("Download", link: "`{ {row.file_url}}`", target: "_blank")

Image Upload Variant

For image uploads with preview:

File Upload (name: "image",
  accept: ".jpg,.jpeg,.png,.gif,.webp",
  maxSize: 10485760,
  preview: true)

After upload, the file_url contains the WordPress media URL. Display with:

Image (src: "`{ {row.file_url}}`")

Variations

Multiple file uploads

Add multiple File Upload widgets with different names:

File Upload (name: "document", label: "Document")
File Upload (name: "cover_image", label: "Cover Image", accept: ".jpg,.png")

Both URLs are available in the form context.

Restrict uploads by role

Require Auth (capability: "upload_files")

The upload_files capability is assigned to Authors and above by default.

Delete uploaded file

Create a delete action pipeline that also removes the WordPress media attachment:

Context → Require Auth → Delete Row (tableId: documents) → Output

WARNING

Always validate file types on the server side, not just the client. The accept attribute on the File Upload widget provides a hint to the browser, but the actual file type validation should happen during the WordPress media upload process.

TIP

Files are uploaded to the WordPress media library, which handles storage, thumbnail generation (for images), and CDN compatibility. The file_url stored in your database table points to the WordPress media URL.