Aphex
Schema Types

Image

Image upload with drag-and-drop, alt text, and asset browser.

The image type handles image uploads. It provides a drag-and-drop upload zone, image preview, alt text editing, and an asset browser for reusing previously uploaded images.

Definition

import type { ImageField } from '@aphexcms/cms-core';
{
  name: 'cover',
  type: 'image',
  title: 'Cover Image'
}

Properties

PropertyTypeRequiredDescription
namestringYesField identifier.
type'image'YesMust be 'image'.
titlestringYesLabel shown in the admin UI.
descriptionstringNoHelp text shown below the label.
acceptstring | string[]NoAllowed MIME types, wildcards, or extensions. Defaults to 'image/*'.
privatebooleanNoIf true, the asset CDN route requires an authenticated session with access to its org.
initialValuestring | () => string | Promise<string>NoDefault value.
validation(Rule) => RuleNoValidation rules.

The type definition also includes hotspot, metadata, and fields properties. These are reserved for future use and are not yet implemented.

Stored Data

Images are stored as a reference to an asset record:

{
  _type: 'image',
  asset: {
    _type: 'reference',
    _ref: 'asset-id'
  }
}

The referenced asset stores the actual file metadata (URL, filename, dimensions, alt text, etc.) separately in the assets table. To turn asset._ref into a usable URL when rendering, see Frontend → resolve asset refs to URLs.

Examples

Basic image

{
  name: 'photo',
  type: 'image',
  title: 'Photo',
  validation: (Rule) => Rule.required()
}

Restricted file types

The same MIME type, wildcard, and extension syntax described for file fields is available here. A comma-separated string and an array are equivalent:

{
  name: 'logo',
  type: 'image',
  title: 'Logo',
  accept: ['image/svg+xml', 'image/png', '.png'],
  description: 'SVG or PNG only.'
}

accept can only narrow the installation-wide allow-list, never widen it. If a type isn't in storage.allowedMimeTypes — or the built-in default when you haven't set one — a field listing it still rejects the upload.

SVG is in that default list, because logos and icons overwhelmingly are SVG. It is safe there only because of how it's served: every image/svg+xml response from /media/{id}/{filename} carries Content-Disposition: attachment and a default-src 'none'; sandbox CSP. An <img src="..."> renders it normally — scripts never execute in an image context — but a browser cannot be navigated to it as a document, which is what would otherwise make an uploaded SVG stored XSS on your own origin.

Private image

Mark a field private when the image should only be served to authenticated members of the owning organization:

{
  name: 'idScan',
  type: 'image',
  title: 'ID Scan',
  private: true
}

Requests to /media/[id]/[filename] for a private asset return 401 unless the request carries a valid session, or a signed URL — the way to show a private image to a visitor with no admin account.

Privacy is resolved from the field the asset was uploaded into. An image uploaded through the media library and then referenced here stays public; upload it through this field instead.

In a document

const product = defineType({
	type: 'document',
	name: 'product',
	title: 'Product',
	fields: [
		{
			name: 'name',
			type: 'string',
			title: 'Name',
			validation: (Rule) => Rule.required()
		},
		{
			name: 'image',
			type: 'image',
			title: 'Product Image'
		},
		{
			name: 'price',
			type: 'number',
			title: 'Price',
			validation: (Rule) => Rule.required().positive()
		}
	]
});

Validation

Available rules: required(), custom(fn), error(msg), warning(msg), info(msg)

Edit on GitHub

Last updated on