@pro-laico/images
Adds the image and favicon upload collections to your Payload admin: web-ready WebP sizes, a favicon picker field, and an image block for your pages.
@pro-laico/images gives your Payload admin a place to upload images and favicons. The image collection converts uploads to WebP and generates a standard set of responsive sizes, a separate favicons collection keeps favicons out of your main image library, and the package ships a favicon-picker field and an image block you can use elsewhere. It's a tool the atomic-payload template and other plugins bring along, but you can add it on its own too.
@pro-laico/images is a Tool package, a building block of the Atomic Payload stack, meant to be used alongside @pro-laico/core and the other @pro-laico/* packages (most easily via the atomic-payload template), not on its own.
Installation
pnpm add @pro-laico/imagesnpm install @pro-laico/imagesyarn add @pro-laico/imagespayload, next, and react are peers you already have in a Payload + Next.js app. @oversightstudio/blur-data-urls is an optional peer. Install it only if you want blur-up placeholders for your images (see Add blur placeholders).
Setup
@pro-laico/core comes bundled with this plugin — it's a dependency, not a separate install. Its cached reads reach Payload through a config you register once in your Next.js instrumentation hook (registerPayloadConfig); see @pro-laico/core → Setup to wire it up.
Adding the image collections to your own Payload project.
Add the plugin to your Payload config
import { buildConfig } from 'payload'
import { imagesPlugin } from '@pro-laico/images'
export default buildConfig({
plugins: [imagesPlugin({ enabled: true })],
})This registers the Images collection (where you upload images, and every upload is converted to WebP and given a set of responsive sizes) and the Favicons collection (a separate place for .ico favicons). Both appear under an Assets group in the admin sidebar. Pass includeFavicons: false if you only want the Images collection.
Upload your images
Open the Images collection in the admin and upload your images. Each one gets an alt text field (required, used as the title) and a focal point. Accepted types are PNG, JPEG, WebP, and AVIF.
Add blur placeholders (optional)
The plugin does not wire up blur placeholders itself, because pnpm doesn't install the optional peer next to this package, so the plugin can't reach it. Install @oversightstudio/blur-data-urls yourself and register it after imagesPlugin, passing the exported Images collection:
import { buildConfig } from 'payload'
import { imagesPlugin, Images } from '@pro-laico/images'
import { blurDataUrlsPlugin } from '@oversightstudio/blur-data-urls'
export default buildConfig({
plugins: [
imagesPlugin({ enabled: true }),
blurDataUrlsPlugin({
enabled: true,
collections: [Images],
blurOptions: { blur: 18, width: 32, height: 'auto' },
}),
],
})That generates a small blurred placeholder for each upload, which the image block can show while the full image loads.
The atomic-payload template already includes @pro-laico/images: the Images and Favicons collections, the blur-placeholder plugin wired against the Images collection, and the image block registered with the other Atomic blocks. You just upload your assets.
Upload images and favicons
Open the Images collection and upload your images (each needs alt text), and the Favicons collection for any .ico favicons.
Pick a favicon
In the Site Metadata global, choose the light and dark favicons for your site. Individual pages can override them in their SEO tab.
Using it in your app
The favicon field
FaviconField is a ready-made upload field that points at the Favicons collection, so you can add a favicon picker to any global or collection. It's what @pro-laico/site uses for the site's favicons; add it to your own config the same way:
import { FaviconField } from '@pro-laico/images'
const SiteMetaData = {
slug: 'siteMetaData',
fields: [FaviconField({ name: 'lightFavicon' }), FaviconField({ name: 'darkFavicon' })],
}Pass a name (and anything else a Payload upload field accepts) to mount more than one picker. The field always targets the favicons collection, so an override can't accidentally re-point it.
The image block
The package ships an Image block (slug ImageChild) that lets editors drop an image into Atomic content. It isn't registered by imagesPlugin. It's one of the default child blocks the Atomic renderer wires up, so in most setups you get it for free. To register it in your own block list, import it from the @pro-laico/images/blocks/imageChild subpath:
import { createImageBlock, Image } from '@pro-laico/images/blocks/imageChild'
// The prebuilt block:
const blocks = [Image]
// …or build your own, prepending/appending fields (e.g. a class-name field):
const customImageBlock = createImageBlock({ prependFields: [], appendFields: [] })The block lets the editor choose the image, a stored size, alt text, and next/image options (priority, blur, loading, and more). On the front end it renders through @pro-laico/atomic/children, which resolves the chosen image and outputs an optimized next/image.
The Image block is not re-exported from the package root. Import createImageBlock or Image from the @pro-laico/images/blocks/imageChild subpath.
Caching & revalidation
The package ships one cached read, getCachedImage, from the @pro-laico/images/cache subpath. Give it an image document id (and an optional size name) and it returns that upload's URL, reading Payload once instead of on every render. The image block calls it so the block looks up the chosen image by id, which keeps the rest of the page cached even when the image changes.
The Images collection revalidates the matching tag for you: saving an image refreshes its cached URL, and deleting one clears it, so the next page render serves the new URL. See Caching & revalidation for how the tags work.
Options
imagesPlugin(options?) accepts:
Prop
Type
imagesOptions and faviconsOptions are standard Payload Partial<CollectionConfig> objects, so any collection key you pass is merged onto the built-in collection. All options at their defaults, as a working starting point:
imagesPlugin({
enabled: true,
includeFavicons: true,
// imagesOptions and faviconsOptions are unset by default (no overrides merged)
})Exports
The plugin
Export
Type
imagesPluginplugin
Parameters
options?:ImagesPluginOptionsSee the Options table above. Everything is optional; omit it to register both collections with their defaults.Returns
PluginA Payload config plugin you add to buildConfig({ plugins: [...] }).Example
import { buildConfig } from 'payload'import { imagesPlugin } from '@pro-laico/images'export default buildConfig({plugins: [imagesPlugin({ includeFavicons: true })],})Location
@pro-laico/imagesImagesPluginOptionstype
Location
@pro-laico/imagesCollections & fields
Export
Type
Imagescollection
Location
@pro-laico/imagesFaviconscollection
.ico), kept separate from the main image library.Location
@pro-laico/imagesFaviconFieldfunction
favicons collection, so an override can't re-target it.Parameters
args?:Partial<UploadField> & { apf?: APFunction[] }Upload-field overrides (name, label, admin, …) merged onto the field; name lets you mount several pickers. apf attaches @pro-laico/core upload hooks via onUploadSetAPF.Returns
UploadFieldA Payload upload field pointing at the favicons collection.Example
import type { GlobalConfig } from 'payload'import { FaviconField } from '@pro-laico/images'// a global with light + dark favicon pickersexport const SiteMetaData: GlobalConfig = {slug: 'siteMetaData',fields: [FaviconField({ name: 'lightFavicon' }), FaviconField({ name: 'darkFavicon' })],}Location
@pro-laico/imagesThe image block
Export
Type
createImageBlockfunction
ImageChild) block. Pass prependFields / appendFields to weave extra fields (e.g. a @pro-laico/styles class-name field) into the start / end of its Image tab.Parameters
options?:ImageBlockOptions{ prependFields?, appendFields? } arrays of Payload fields spread at the start / end of the Image tab. Omit it to get the block with no extra fields.Returns
BlockA Payload block (slug ImageChild) you add to a block field or an Atomic child-block list.Example
import type { Block } from 'payload'import { createImageBlock } from '@pro-laico/images/blocks/imageChild'import { ClassNameField } from '@pro-laico/styles/fields/className'// the Image block with an authored class-name field prependedconst imageBlock: Block = createImageBlock({ prependFields: [ClassNameField({ namePrefix: 'image' })] })Location
@pro-laico/images/blocks/imageChildImageblock
createImageBlock() returns by default.Location
@pro-laico/images/blocks/imageChildImageBlockOptionstype
createImageBlock options (prependFields / appendFields).Location
@pro-laico/images/blocks/imageChildImageChildcomponent
next/image. The Atomic children renderer wires it up for the ImageChild slug, so you rarely import it directly.Parameters
props:RenderChild<ImageChild>The block data and pass-through props the Atomic children renderer supplies (block, pt).Returns
Promise<JSX.Element>A next/image for the chosen image (resolved through getCachedImage), or a placeholder when no image is set.Example
import { ImageChild } from '@pro-laico/images/blocks/imageChild/component'// register it for the ImageChild slug in your Atomic children map;// the renderer passes the block + pass-through props for youconst childComponents = {ImageChild,// ...other child block components}Location
@pro-laico/images/blocks/imageChild/componentCache getter
Export
Type
getCachedImagefunction
version size if given, else the original. Wrapped in withCache under the image tag and revalidated when the image is saved or deleted, so a page reads it once instead of re-querying Payload on every render. Resolves the config the app registered with registerPayloadConfig.Parameters
tid:string | null | undefinedThe Images document id (e.g. block.image.id). A falsy id returns an empty string.version?:string | nullA stored size name (thumbnail, square, small, medium, large, xlarge, og). Omit it for the original upload URL.Returns
Promise<string | undefined>The image URL, or undefined when the document or size can't be resolved.Example
import Image from 'next/image'import { getCachedImage } from '@pro-laico/images/cache'// inside the ImageChild server component, the block stores the image as a relationshipexport async function Picture({ block }) {const src = await getCachedImage(block.image?.id, block.version)if (!src) return <div className="w-full h-full bg-gray-200" />return <Image alt={block.alt ?? ''} src={src} />}Location
@pro-laico/images/cacheTypes
Export
Type
Image (type)type
Image), for typed access to your image data.Location
@pro-laico/images/schemaRelated
@pro-laico/site
The ready-made site shape for Atomic Payload: Pages, Header, and Footer collections plus site-wide Settings and SEO metadata, all from one sitePlugin().
@pro-laico/mux-video
Lets editors upload videos in the Payload admin and drop them onto pages. Mux handles the encoding and adaptive streaming, and a ready-made video block renders the player.