Reference
Every plugin option, environment variable, CLI command, endpoint, and export in one place.
imagesPlugin(options?) is the single entry point. One call registers the collections, the
transform and purge endpoints, and the admin UI. It's zero-config — everything below is a knob you
can turn later.
Plugin options
The shape splits along one seam: the collections the plugin registers live under collections, and
the render engine's knobs live under options. Every collections.<name> takes the same three keys
— slug renames it, overrides is a Payload CollectionConfig passthrough the merge kit applies,
and options (on images) is the plugin's own knobs for that collection. Optional sub-features
read false | Options: options.prewarm: {} is "on, all defaults", options.prewarm: false opts
out.
The Reference tab is the interactive view (collections and options expand to their nested
options); TypeScript is the same shape in code, every option at its default.
enabledbooleandefault trueWhen false, registers nothing. This is "not installed", not "paused": on SQL adapters, turning it off for an existing project produces a migration that drops the image tables and their data.
collections{ images?, generatedImages?, renderProfiles? }
The collections the plugin registers, one key each. Every key takes the same shape: slug renames the collection (the plugin repoints every internal reference at the new name), overrides is a Payload CollectionConfig passthrough merged onto the plugin's (upload/access/admin/custom shallow-merged, fields/hooks appended, defaultPopulate/forceSelect merged as selects — which matters on images, where the plugin sets both — everything else replaced), and images additionally takes options — its own knobs.
imagesCollectionOption<ImagesCollectionOptions>
The Images collection you touch.
slugstringdefault 'images'Rename it; the plugin repoints every internal reference (the variant cache's source relationship, the variants join, the purge hooks, the endpoints, the marker) at the new name.
overridesPartial<CollectionConfig>Payload config merged onto the plugin's by the shared merge rules. Rename with the sibling slug key, not here. Don't redeclare a base field name like alt or variants — that's a named boot error.
optionsImagesCollectionOptions
This collection's own knobs.
focalUIfalse | { previewRatios?: string[] }default {}Render the focal-point picker + ratio-preview field and the preset manager panel (preset toggles, cached-variant list, purge button, per-image variant cap). The object form customizes the aspect ratios shown as preview tiles (default ['16:9','9:16','1:1','4:3','3:2','21:9']). false leaves a clean upload collection, and the import map isn't needed.
foldersbooleandefault truePayload's native folder organization on the images collection, so editors can organize a large library. On by default — a managed image library without organization is the wrong default. It adds a nullable folder relationship and Payload's hidden payload-folders collection: additive, so an existing database needs a schema push but no data migration. Set false to opt out.
localizeAltbooleandefault whether the app configures localizationMark the alt field localized (requires Payload localization). Defaults to Boolean(config.localization) — a localized site localizes its alt text, an accessibility fact rather than a preference. Flipping it on an existing collection is a data migration, so set it explicitly to opt out.
mimeTypesstring[]default ['image/avif','image/webp','image/jpeg','image/png']Accepted upload mime types for the images collection; defaults to the raster formats the transform pipeline can process. Widen it (e.g. add 'image/svg+xml') or narrow it, but the endpoint only meaningfully resizes/crops raster images; non-raster uploads are stored and served as-is.
maxOriginalSizenumberCap the stored original's longest edge (px), applied once on upload. Off by default; your original stays untouched (the collection can double as original storage). Set it only to bound storage.
generatedImagesCollectionOption
The hidden generated-images (variant cache) collection. No plugin options.
slugstringdefault 'generated-images'Rename it; the variants join, admin panel, and marker follow.
overridesPartial<CollectionConfig>Payload config merged onto the plugin's by the shared merge rules. Rename with the sibling slug key, not here.
renderProfilesCollectionOption
The image-render-profiles collection that prewarming records its observations in. Only registered while options.prewarm is on — prewarm governs whether the collection exists, this key only shapes it. No plugin options.
slugstringdefault 'image-render-profiles'Rename it; the recorder, the prewarm task, and the marker follow.
overridesPartial<CollectionConfig>Payload config merged onto the plugin's by the shared merge rules. Rename with the sibling slug key, not here.
optionsImagesOptions
The render engine — the on-demand transform endpoint, its prewarming, and the variant/preset ladder.
transformTransformEndpointConfigdefault {}
Config for the on-demand transform + purge endpoints. The endpoint is always registered — it is the plugin.
cdnCacheControlbooleandefault trueAlso emit CDN-Cache-Control / Vercel-CDN-Cache-Control headers (public images only).
maxDimensionnumberdefault 4096Hard ceiling on either output dimension.
defaultQualitynumberdefault 90Encode quality used when a request omits q.
qualityRange[number, number]default [40, 95]Allowed quality range; requested values clamp into it.
defaultFormatFormatdefault 'auto'Output format when a request omits fmt (auto negotiates from Accept).
formatsFormat[]default ['auto','avif','webp','jpeg','png']Output formats the endpoint may emit.
preferAvifbooleandefault falseAuto-negotiate AVIF when accepted. Off by default: AVIF encodes far slower, so fmt=auto serves WebP for a fast cold path (AVIF stays available on explicit fmt=avif).
maxInputPixelsnumberdefault 100000000Max source pixels Sharp will decode: a decompression-bomb guard that also caps per-transform memory (~100MP ≈ 400MB).
maxConcurrencynumberdefault cpus - 1 (computed)Max concurrent Sharp transforms in this process (or IMAGES_TRANSFORM_CONCURRENCY). Computed from the host CPU count.
sharpConcurrencynumberdefault 1Per-image libvips thread cap (or IMAGES_SHARP_CONCURRENCY); 0 = CPU cores. Defaults to 1 for serverless safety.
fallbackbooleandefault trueOn a cache miss with a NEARBY variant ready — same fit and focal point, ratio within 8% drift, at least half the effective request width, any quality, in a format the client's negotiation proved it decodes — serve it immediately with Cache-Control: no-store while the exact variant generates in the background. The next request gets the exact one; the stand-in is never cached or persisted.
prewarmfalse | PrewarmOptionsdefault {}Smart prewarming, driven by a strategy: warm the built-in renders, seeds (default: a square 1:1 q80 crop at the derived widths — declaring your own replaces it), and learned traffic profiles for new/replaced/re-focused images via a deferred Payload Job. On by default (a cold variant is a visible LCP hit on image-led pages); the object form takes strategy / formats / maxVariantsPerImage — the strategy carries widths ('srcset', { every: n }, or a list), builtIns, learned, seeds, onUpload, autoRun, autoRunLimit, and queue — and prewarm: false opts out entirely. Being on registers the hidden image-render-profiles collection (a schema change — regenerate types), the imagesPrewarm jobs task, the images:prewarm CLI, and by default the autoRun runners on the plugin-owned images-prewarm queue: a 5-minute cron (50 jobs per firing) on long-lived processes, plus a post-upload kick and an image-traffic drain that keep jobs running on serverless too — see Prewarming → When it runs.
pixelStepnumber | number[]default 50Project-wide srcset widths and the endpoint's snap grid. The default 50 steps srcset densely (every 50px up to the source width) and snaps freeform requests to the same grid — prewarm warms an every-5th skeleton and the fallback bridges the rest. An array switches srcset to a fixed breakpoint ladder (e.g. next/image's deviceSizes [640, 750, 828, 1080, 1200, 1920, 2048, 3840]) whose widths always pass the snap unchanged — smaller responses on wide originals, and prewarm warms the ladder exactly. maxDimension caps the top.
presetTemplatesRecord<string, PresetSpec>default { og, thumbnail }Named, reusable variant specs ({ width?, height?, aspectRatio?, fit?, quality?, format? }) that editors toggle onto images by name and serve via /api/img/:id?preset=<name>. Guaranteed (cap-exempt) and eagerly pre-generated on upload. Two defaults ship unless overridden: og (1200×630 cover jpeg, q80) and thumbnail (160×160 cover webp, q70 — backs the admin thumbnail). See Caching → Guaranteed presets.
variantLimitnumberdefault 200Per-image cap on cached variants — bounds storage from the public endpoint. Each image gets a variantLimit field defaulting to this; past the cap a new freeform size is served from a nearby variant (or generated but not stored) instead of adding a row. Presets are exempt. See Caching → The variant cap.
accessImagesAccessOptions
Per-endpoint gates for the plugin's HTTP endpoints — one EndpointAccess ((req) => boolean | Promise<boolean>) per endpoint. The source doc's own read access is still enforced on top of both. See Gating endpoints.
manageEndpointAccessdefault any logged-in userGates the per-source admin endpoints (/img/purge/:id, /img/presets/:id, /img/prewarm/:id).
serveEndpointAccessdefault publicGates the transform/serve endpoint (GET /img/:id). Public by default — image serving must answer anonymous traffic; the source collection's read access still applies.
import { imagesPlugin } from '@pro-laico/payload-images'
// Every option at its default. This is the zero-config behaviour, written out.
imagesPlugin({
enabled: true,
collections: {
images: {
// slug: 'media', // optional, no default: renames the collection (references follow)
// overrides: { /* … */ }, // optional, no default: a Payload CollectionConfig merged onto it
options: {
focalUI: {}, // or { previewRatios: ['16:9', '9:16', '1:1', '4:3', '3:2', '21:9'] }
folders: true,
// localizeAlt: false, // default: whether your config sets `localization`
mimeTypes: ['image/avif', 'image/webp', 'image/jpeg', 'image/png'],
// maxOriginalSize: 4096, // optional, no default: off (the original is kept untouched)
},
},
// generatedImages: { slug: 'variant-cache' }, // optional: rename / override the variant cache
// renderProfiles: { slug: 'render-shapes' }, // optional: rename / override the prewarm registry
},
options: {
transform: {
cdnCacheControl: true,
maxDimension: 4096,
defaultQuality: 90,
qualityRange: [40, 95],
defaultFormat: 'auto',
formats: ['auto', 'avif', 'webp', 'jpeg', 'png'],
preferAvif: false,
maxInputPixels: 100_000_000,
// maxConcurrency: cpus - 1, // default (computed from host CPUs, or IMAGES_TRANSFORM_CONCURRENCY)
sharpConcurrency: 1,
fallback: true,
},
prewarm: {
// or false to opt out; `strategy: 'default'` ≡ `strategy: {}` ≡ this, written out:
strategy: {
widths: { every: 5 }, // the numeric-grid default — 'srcset' with an array pixelStep, or an explicit number[]
builtIns: true, // warm src / thumbnailURL / placeholderURL
learned: true, // warm observed render profiles
seeds: [{ aspectRatio: '1:1', quality: 80 }], // declaring your own replaces this default; [] disables
onUpload: true, // enqueue on create / file replace / focal edit
autoRun: '*/5 * * * *', // the plugin runs its own jobs: this cron on long-lived processes, upload kicks + traffic drains on serverless — false turns all three off
autoRunLimit: 50, // jobs (images) per cron firing / upload kick
queue: 'images-prewarm', // plugin-owned, so the plugin's runners only ever run its own jobs
},
formats: ['webp'], // +'avif' automatically when transform.preferAvif
maxVariantsPerImage: 32,
},
pixelStep: 50, // the dense grid — or an array ladder, e.g. [640, 750, 828, 1080, 1200, 1920, 2048, 3840]
presetTemplates: {
// both ship by default; your own entries merge on top
og: { width: 1200, height: 630, fit: 'cover', quality: 80, format: 'jpeg' },
thumbnail: { width: 160, height: 160, fit: 'cover', quality: 70, format: 'webp' },
},
variantLimit: 200,
// access: { // per-endpoint gates for the plugin's HTTP endpoints
// manage: ({ user }) => Boolean(user), // default: any logged-in user (source read still enforced)
// serve: () => true, // default: public (source read still applies)
// },
},
})The named option types — Format, TransformEndpointConfig, PresetSpec, PrewarmOptions, and
PrewarmStrategy — all import from @pro-laico/payload-images.
enabled: false means "not installed", not "paused". On SQL adapters, turning it off for an
existing project produces a migration that drops the image tables — including every cached
variant.
Environment variables
These set per-process defaults without changing code (handy for serverless). An explicit
options.transform.* value wins over the matching env var — the env var is the default when the
option is unset.
| Variable | Default | What |
|---|---|---|
IMAGES_TRANSFORM_CONCURRENCY | cpus - 1 (computed) | Max concurrent Sharp transforms in this process. The default for transform.maxConcurrency. |
IMAGES_TRANSFORM_MAX_QUEUE | max(64, concurrency × 32) | Queued transforms allowed before the endpoint sheds load with a 503. |
IMAGES_SHARP_CONCURRENCY | 1 | Per-image libvips thread cap; 0 = CPU cores. The default for transform.sharpConcurrency. |
IMAGES_PREWARM_FLUSH_MS | 30000 | Debounce before buffered prewarm observations flush to the render-profiles collection (armed by the first observation after a flush). Newly seen widths write on flush; pure hit-count bumps persist at most every 15 minutes per profile. |
NEXT_PUBLIC_SERVER_URL | — | Base URL for generated image URLs. |
VERCEL_PROJECT_PRODUCTION_URL | — | Fallback base URL when NEXT_PUBLIC_SERVER_URL is unset. |
CLI commands
The plugin registers two Payload CLI commands (run via pnpm payload <command>).
| Command | Flags | What |
|---|---|---|
payload images:backfill | --force, --focal, --collection | Stamps upload-time metadata (placeholder tiers, palette, alpha flags, saliency focal point) onto images that predate the plugin. See Metadata → Backfilling an existing library. |
payload images:prewarm | --now, --limit, --collection, --queue | Enqueues one prewarm job per source for a runner to execute; --now generates inline instead, no runner needed. Requires prewarm on. See Prewarming. |
Endpoints
GET /img/:id is gated by options.access.serve (public by default); the per-source admin endpoints
(purge, presets, prewarm) by options.access.manage (any logged-in user). The source doc's own read
access is enforced on top of both. See Gating endpoints.
| Route | What |
|---|---|
GET /api/img/:id | The on-demand transform endpoint: reads the image id and the transform query params, renders the variant once, then caches it. See Image URLs. |
POST /api/img/purge/:id | Purge this image's cached variants (the Preset manager panel's purge button posts here). |
GET /api/img/presets/:id | Which presets exist for this image, each mapped to its cache key and cached variant. |
GET /api/img/prewarm/:id | Prewarm plan and job status (pending job, last run). Registered only while options.prewarm is on. |
Exports
| Export | From | What |
|---|---|---|
imagesPlugin | @pro-laico/payload-images | The Payload plugin; put imagesPlugin() in your config's plugins. |
ResponsiveImage | @pro-laico/payload-images/components/image | The passive responsive <img> component. |
createImageFor | @pro-laico/payload-images | Seed once with your Payload handle → imageFor(id).aspectRatio('16:9').blur('md').fetch() resolves the render-ready doc for <ResponsiveImage>. |
RESPONSIVE_IMAGE_SELECT | @pro-laico/payload-images | The lean select for a render-declared read (alt + src + aspectRatio + srcset + placeholder) — for writing the findByID yourself. |
getImageUrl | @pro-laico/payload-images/utils/urls | Build one transform URL for an image (an id or a populated doc): OG tags, CSS backgrounds, emails. |
buildSrcset | @pro-laico/payload-images/utils/urls | Build a responsive srcset + default src for an image (an id or a populated doc — a doc also supplies the width cap and cache-busting token). |
| URL-builder types | @pro-laico/payload-images/utils/urls | GetImageUrlOptions, ImageResource, BuildSrcsetOptions, BuildSrcsetResult, BuildUrlOptions, plus Fit and Format re-exported alongside them. |
| Component prop types | @pro-laico/payload-images/components/image | ImageProps, ResponsiveImageProps — for typing a wrapper around <ResponsiveImage>. |
FocalPreview | @pro-laico/payload-images/admin/focalPreview | Admin focal-point picker + ratio/placeholder preview field (wired via the import map when collections.images.options.focalUI is on). |
PresetManager | @pro-laico/payload-images/admin/presetManager | Admin Preset manager panel — preset toggles, cached-variant list, purge button, and the per-image variant cap (wired via the import map when collections.images.options.focalUI is on). |
PrewarmOptions / PrewarmStrategy / PrewarmStrategyConfig / PrewarmWidths / ResolvedPrewarmStrategy / RenderProfileSeed | @pro-laico/payload-images | The prewarm option's types: the config shape, the strategy (what to warm and when it runs, with its width-axis union), and a pinned cold-start render profile. |
PresetSpec / PresetEntry | @pro-laico/payload-images | The presets types: a presetTemplates spec and a per-image presets array entry. |
PREWARM_TASK_SLUG | @pro-laico/payload-images | The prewarm jobs task slug (imagesPrewarm) — for queueing/running the job yourself. |
readImagesMarker | @pro-laico/payload-images | Read the resolved config (slugs + settings) off config.custom.payloadImages from a script or app — readImagesMarker(config).sourceSlug. |
GENERATED_IMAGES_SLUG / IMAGE_RENDER_PROFILES_SLUG | @pro-laico/payload-images | The default slugs of the variant-cache and render-profiles collections (before any slug rename). |
| Option & marker types | @pro-laico/payload-images | ImagesPluginOptions, ImagesOptions, ImagesCollectionOptions, ImagesAccessOptions, TransformEndpointConfig, PayloadImagesMarker, PayloadImagesPrewarmMarker. |
EndpointAccess | @pro-laico/payload-images | The endpoint-gate function type (req) => boolean | Promise<boolean>; see Gating endpoints. |
| Render-contract types | @pro-laico/payload-images | ImageFor, ImageForChain, ImageSource, ResponsiveImageDoc, AspectRatio, Fit, Format, OutputFormat, ImageRenderContext, ImageRenderIntent, BlurRenderIntent, ImagePalette, PaletteSwatch, PlaceholderFormat, PlaceholderQuality. |
Collections & globals
The plugin registers images (the upload collection you touch), the hidden generated-images
variant cache, and — with options.prewarm on — the hidden image-render-profiles registry, all
under an Assets admin group. collections.images.options.folders brings Payload's own hidden
payload-folders collection with it. See Collections for
their fields, hooks, and access.