Media fields
Reference uploads from your collections.
Media field types let your own collections reference uploads: image and file hold one upload, and images and files hold an ordered list. Each stores the upload's UUID, exactly as a relation does, and checks the referenced file against the limits you declare.
import { defineCollection, field } from 'ohnejs';
export default defineCollection({
fields: {
title: field('text'),
cover: field('image', { maxSize: '5mb', minWidth: 1200 }),
attachments: field('files', { types: ['document'], max: 10 }),
},
});In the dashboard, each one shows a picker for the media library and a direct upload button. A file that the field cannot accept is greyed out, with the reason.
Options
image and images accept only images and add pixel limits. file and files accept anything unless types narrows them.
types- the media types the field accepts. Defaults to['image']on the image fields, and to any file on the others.minSize,maxSize- file size limits, in bytes or as a string like'5mb'.minWidth,maxWidth,minHeight,maxHeight- pixel limits, image fields only.onDelete- what happens when the upload is deleted, as for relations:setNullby default onimageandfile, andcascadeonimagesandfiles.allowEmpty,min,max- list fields only. They limit how many links a written list holds.
Types
A types entry is an exact media type (image/png), a top-level wildcard (image/*), or a category name: image, video, audio, document, archive, font, text, code. A category groups the media types a person would expect under it, so ['document'] takes PDFs and office formats without listing them. uploads.types in config uses the same grammar.
Validation
A write checks each referenced upload against the field's options, in one read. The upload must be a file, an image for the image fields, and within types, the size limits, and the pixel limits. An image whose width and height could not be read passes the pixel limits.
A failure is reported at the field, or at attachments[2] for the third item of a list, with a message such as The file must be at most 5 MB. A UUID that belongs to no upload fails like any other broken reference.
Reading
Populate the field to get the upload's record, decorated with url, with variants when an image service is configured, and, with UPLOADS_SECRET set, with expires for a private file:
const post = await query('Posts').populate('cover').where('UUID', id).findFirst();
post.cover.url;
// -> '/uploads/photos/sunset.jpg'
post.cover.variants.thumbnail;
// -> 'https://img.example.com/.../w_320,h_320,fit_inside,f_webp/photos/sunset.jpg'A private file's url and variants stop working at expires, so render them per request rather than storing them.
Over the collections API, a media field populates only for a caller who may read Uploads. Anyone else, including a visitor who is not signed in, gets null for image and file and an empty list for images and files. To serve media fields to everyone, make that read public.
For a page of your own, imageURL builds a variant URL and imageSrcSet builds a srcset. Without a service, both fall back to the original's URL, so a template needs no special case.