Fields
File Field
The file field renders a file upload input in the admin UI. It can reference an upload-enabled collection (recommended) or store a plain URL string (legacy mode). Use it for PDFs, documents, spreadsheets, and any non-image file type. For images specifically, use f.image() instead.
Config options
| Option | Type | Default | Description |
|---|---|---|---|
required | boolean | false | Makes the field required in both the schema and the admin UI. |
readOnly | boolean | false | Renders the input as disabled. |
relationTo | string | — | Slug of the upload collection to reference (e.g. "documents"). When set, uses the full upload system. |
accept | string[] | — | Accepted MIME types (e.g. ["application/pdf"]). |
maxSize | number | — | Maximum file size in bytes. |
displayPreview | boolean | true | Show a file preview/icon in the admin UI. |
hasMany | boolean | false | Allow multiple file uploads. |
label | string | (data) => string | Auto-generated | Custom label for the admin UI. |
description | string | — | Help text displayed below the label. |
condition | FieldCondition | — | Conditionally show or hide this field. |
sidebar | boolean | false | Place this field in the document sidebar. |
Convex validator
- With
relationTo: delegates tof.upload()internally, stores asv.string()(file record ID) orv.array(v.string())ifhasMany. - Without
relationTo(legacy): stores asv.string()(plain URL) orv.array(v.string())ifhasMany.
Example usage
import { f, defineVextroCollection } from "vextro";
export const resources = defineVextroCollection({
slug: "resources",
label: "Resources",
collectionType: "content",
tableName: "resources",
fields: {
title: f.text({ required: true }),
// Upload collection integration (recommended)
attachment: f.file({
relationTo: "documents",
accept: ["application/pdf", "application/msword"],
maxSize: 25 * 1024 * 1024, // 25MB
}),
// Multiple files
supportingDocs: f.file({
relationTo: "documents",
hasMany: true,
}),
},
}); Difference from upload and image
| Field | Purpose | Default MIME filter |
|---|---|---|
f.upload() | Generic upload with explicit relationTo (required) | None |
f.image() | Image uploads with image/* filter | image/* |
f.file() | Non-image files, optional relationTo | None |
When relationTo is provided, f.file() delegates internally to f.upload(). The key difference is that f.file() does not default to image MIME types and can also work in legacy mode (plain string storage) when relationTo is omitted.
Upload collection requirement
To use the upload system, you need an upload-enabled collection:
export const documents = defineVextroCollection({
slug: "documents",
label: "Documents",
collectionType: "config",
tableName: "documents",
upload: true, // enables upload handling
fields: {
title: f.text(),
description: f.textarea(),
},
});