Payload Plugins
Pluginspayload-images

Collections

The collections the plugin registers, how to extend your own upload collection, and how images seed natively.

For AI / LLMs: View Markdown

The plugin registers three collections under an Assets admin group: one you upload to, one that caches what gets generated, and — because options.prewarm is on by default — the hidden render-profile registry (an explicit prewarm: false drops it). The upload collections accept PNG, JPEG, WebP, and AVIF. collections.images.options.folders is on by default, so Payload's own hidden payload-folders collection is registered too.

Collections

images

The source upload, and the only one you touch. It stores the original untouched (no pre-generated sizes) and keeps Payload's native focal point; every rendered size is produced on demand by the endpoint and recorded in generated-images. Read is public; writes are logged-in-admin only.

Fields

Prop

Type

The presetManager UI field renders the Presets & variants panel — the admin surface for the presets, variantLimit, and variants fields above:

The Presets & variants panel on an image doc, showing preset toggles, the cached-variant list, purge controls, and the variant limitThe Presets & variants panel on an image doc, showing preset toggles, the cached-variant list, purge controls, and the variant limit

Hooks

Prop

Type

generated-images

The hidden, durable variant cache that you never touch directly. The transform endpoint writes one upload doc here per (source, settings, focal) combination, keyed by a unique cacheKey and related back to its source. It's an upload collection, so each generated variant is stored through whatever storage adapter you've configured. It's surfaced on images via the variants join and purged by that collection's change/delete hooks.

Fields

Prop

Type

Hooks: none. Variants are derived and disposable, so the collection carries no revalidation hooks (busting cache tags on every cache-miss create would be pure churn).

Variant reads inherit the source's access policy. generated-images read access requires an authenticated user AND defers to the source collection's read access, re-rooted through the source relationship — so a tenant-scoped or owner-only images policy scopes the cached variants (docs and bytes) automatically. Caveat: a source read access that branches on the individual doc id can't be re-rooted; in that case supply your own collections: { generatedImages: { overrides: { access: { read: … } } } }. The transform endpoint itself always gates on the source's read access.

image-render-profiles

Registered only with prewarm on: the hidden registry of render profiles the transform endpoint has actually served — one doc per distinct (ratio, fit, quality, format) shape, carrying an approximate hitCount, a lastSeenAt (profiles unseen for a while stop being warmed), and a capped per-width observation histogram. Written only by the endpoint's buffered recorder; read by the prewarm job/CLI. Small by construction: the endpoint's snap grid and quality buckets bound the distinct-profile space. Like the variant cache, it carries no revalidation hooks.

Reshape it with collections.renderProfiles, same as the other two. options.prewarm is what decides whether it exists at all — with options: { prewarm: false } there's no collection for the key to shape.

Renaming a collection, and adding your own fields

Most projects already call their upload collection media. Rename the one the plugin registers and add your fields to it — slug is a first-class override, and the plugin repoints every internal reference at the new name:

imagesPlugin({
  collections: {
    images: {
      slug: 'media',
      overrides: { fields: [{ name: 'credit', type: 'text' }] },
    },
  },
})

That registers one collection, media, carrying the whole pipeline (the focal UI, the variants join, the purge hooks, upload.focalPoint) plus your credit field. The transform endpoint serves /api/img/:id for media docs, the variant cache's source relationship points at media, and readImagesMarker(config).sourceSlug reads back 'media'.

The variant cache renames the same way:

imagesPlugin({ collections: { generatedImages: { slug: 'image-variants' } } })

slug, overrides, and options — one key each. Every collections.<name> splits the same way: slug renames it, overrides is a plain Partial<CollectionConfig> (upload, access, admin, custom shallow-merge over the plugin's, fields and hooks append after them, defaultPopulate and forceSelect merge as selects, everything else replaces), and options is the plugin's own knobs for the collection. So a collections.images.options.mimeTypes / maxOriginalSize and a collections.images.overrides.upload both reach upload — set either, and the explicit overrides.upload wins. (localizeAlt isn't one of them: it localizes the alt field, not upload.)

Don't redeclare a field the plugin injects. overrides.fields append, so a field of yours named alt, variants, placeholder, palette, … is a boot error naming the plugin, the collections key, and the field — rename yours instead.

Seeding

Because images is a plain Payload upload collection, it seeds natively through @pro-laico/payload-seed: defineSeed('images', …) with each record carrying its source file on _file, referenced from a page with ref('images', …):

import { defineSeed, seedPlugin } from '@pro-laico/payload-seed'
import { imagesPlugin } from '@pro-laico/payload-images'

// src/seed/images.ts — focal points drive the on-demand crops from the first serve
const images = defineSeed('images', ({ file }) => [
  { _key: 'hero', _file: file('hero.jpg'), alt: 'Hero', focalX: 78, focalY: 32 },
])

// src/seed/pages.ts — reference a seeded image (an upload-field relationship)
const pages = defineSeed('pages', ({ ref }) => [
  { _key: 'home', title: 'Home', heroImage: ref('images', 'hero') },
])

plugins: [
  imagesPlugin(),
  seedPlugin({ definitions: [images, pages], options: { assetsDir: 'seed-assets' } }),
]

alt, focalX, and focalY are ordinary record fields, type-checked against the collection. Payload's folders are on by default (collections.images.options.folders) and seed the same way — ordinary docs in the hidden payload-folders collection, each image referencing its folder like any relationship. See examples/images-sandbox for a full working setup, and the payload-seed docs for everything else about seeding.

On this page