Payload Plugins
Pluginspayload-images

Reference

Every plugin option, environment variable, CLI command, endpoint, and export in one place.

For AI / LLMs: View Markdown

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 true

When 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 true

Payload'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 localization

Mark 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.

maxOriginalSizenumber

Cap 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 true

Also emit CDN-Cache-Control / Vercel-CDN-Cache-Control headers (public images only).

maxDimensionnumberdefault 4096

Hard ceiling on either output dimension.

defaultQualitynumberdefault 90

Encode 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 false

Auto-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 100000000

Max 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 1

Per-image libvips thread cap (or IMAGES_SHARP_CONCURRENCY); 0 = CPU cores. Defaults to 1 for serverless safety.

fallbackbooleandefault true

On 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 50

Project-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 200

Per-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 user

Gates the per-source admin endpoints (/img/purge/:id, /img/presets/:id, /img/prewarm/:id).

serveEndpointAccessdefault public

Gates 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.

VariableDefaultWhat
IMAGES_TRANSFORM_CONCURRENCYcpus - 1 (computed)Max concurrent Sharp transforms in this process. The default for transform.maxConcurrency.
IMAGES_TRANSFORM_MAX_QUEUEmax(64, concurrency × 32)Queued transforms allowed before the endpoint sheds load with a 503.
IMAGES_SHARP_CONCURRENCY1Per-image libvips thread cap; 0 = CPU cores. The default for transform.sharpConcurrency.
IMAGES_PREWARM_FLUSH_MS30000Debounce 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_URLBase URL for generated image URLs.
VERCEL_PROJECT_PRODUCTION_URLFallback base URL when NEXT_PUBLIC_SERVER_URL is unset.

CLI commands

The plugin registers two Payload CLI commands (run via pnpm payload <command>).

CommandFlagsWhat
payload images:backfill--force, --focal, --collectionStamps 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, --queueEnqueues 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.

RouteWhat
GET /api/img/:idThe 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/:idPurge this image's cached variants (the Preset manager panel's purge button posts here).
GET /api/img/presets/:idWhich presets exist for this image, each mapped to its cache key and cached variant.
GET /api/img/prewarm/:idPrewarm plan and job status (pending job, last run). Registered only while options.prewarm is on.

Exports

ExportFromWhat
imagesPlugin@pro-laico/payload-imagesThe Payload plugin; put imagesPlugin() in your config's plugins.
ResponsiveImage@pro-laico/payload-images/components/imageThe passive responsive <img> component.
createImageFor@pro-laico/payload-imagesSeed 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-imagesThe lean select for a render-declared read (alt + src + aspectRatio + srcset + placeholder) — for writing the findByID yourself.
getImageUrl@pro-laico/payload-images/utils/urlsBuild one transform URL for an image (an id or a populated doc): OG tags, CSS backgrounds, emails.
buildSrcset@pro-laico/payload-images/utils/urlsBuild 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/urlsGetImageUrlOptions, ImageResource, BuildSrcsetOptions, BuildSrcsetResult, BuildUrlOptions, plus Fit and Format re-exported alongside them.
Component prop types@pro-laico/payload-images/components/imageImageProps, ResponsiveImageProps — for typing a wrapper around <ResponsiveImage>.
FocalPreview@pro-laico/payload-images/admin/focalPreviewAdmin 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/presetManagerAdmin 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-imagesThe 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-imagesThe presets types: a presetTemplates spec and a per-image presets array entry.
PREWARM_TASK_SLUG@pro-laico/payload-imagesThe prewarm jobs task slug (imagesPrewarm) — for queueing/running the job yourself.
readImagesMarker@pro-laico/payload-imagesRead 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-imagesThe default slugs of the variant-cache and render-profiles collections (before any slug rename).
Option & marker types@pro-laico/payload-imagesImagesPluginOptions, ImagesOptions, ImagesCollectionOptions, ImagesAccessOptions, TransformEndpointConfig, PayloadImagesMarker, PayloadImagesPrewarmMarker.
EndpointAccess@pro-laico/payload-imagesThe endpoint-gate function type (req) => boolean | Promise<boolean>; see Gating endpoints.
Render-contract types@pro-laico/payload-imagesImageFor, 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.

On this page