@bl0cka.de/payload-image-gallery-field (0.1.6)
Installation
@bl0cka.de:registry=npm install @bl0cka.de/payload-image-gallery-field@0.1.6"@bl0cka.de/payload-image-gallery-field": "0.1.6"About this package
@bl0cka.de/payload-image-gallery-field
Visual image gallery field for Payload CMS v3: a thumbnail grid with drag reorder, multi-select picking, in-place uploads, and inline image editing — built almost entirely on Payload's own admin primitives, so it follows the admin UI's look, theming (light/dark), i18n, and permissions automatically.
Why
Payload's native upload field with hasMany: true is great data-model-wise,
but renders selected media as text rows — unusable for curating an image
gallery visually. This field keeps the exact same data model (an array of
media document IDs) and only replaces the admin component.
Because the data model is the native one:
required,minRows,maxRowsvalidation works out of the box- REST/GraphQL output is identical to a native upload field
- removing the plugin falls back to the native UI without a migration
Features
- Thumbnail grid with square previews (Payload
Thumbnailcomponent) - Drag & drop reorder via Payload's dnd-kit
DraggableSortable(same as the native admin — keyboard accessible) - Choose from existing via Payload's
ListDrawer: full list view with search, filters, pagination, and bulk row selection - Upload in place via Payload's
BulkUploaddrawer — including dropping files straight onto the field (Dropzone) - Click a thumbnail to edit the image document in a
DocumentDrawer(alt text, caption fields, crop / focal point — whatever the collection has); composes nicely with@bl0cka.de/payload-alt-text-plugin - Already-selected images are filtered out of the picker
- Respects
readOnly, collection create permissions, locale, and drafts - Uses Payload's built-in translation keys — no hardcoded strings
Install
npm install @bl0cka.de/payload-image-gallery-field
Usage
import { galleryField } from '@bl0cka.de/payload-image-gallery-field'
export const Projects: CollectionConfig = {
slug: 'projects',
fields: [
galleryField({
name: 'gallery',
label: 'Bildergalerie',
relationTo: 'images',
}),
],
}
After adding the field, regenerate the import map:
npx payload generate:importmap
Options
| Option | Type | Default | Description |
|---|---|---|---|
name |
string |
'gallery' |
Field name |
relationTo |
CollectionSlug |
— (required) | Upload collection the gallery references (single collection) |
label |
Payload label | — | Field label (string, i18n record, or function) |
required |
boolean |
— | Native Payload validation |
minRows / maxRows |
number |
— | Native Payload validation; the UI hides "add" when maxRows is reached |
localized |
boolean |
— | Localize the selection per locale |
filterOptions |
upload filterOptions |
— | Restrict pickable documents |
admin |
UploadField['admin'] |
— | Description, position, condition, … |
allowCreate |
boolean |
true |
Show upload/create UI (still gated by the user's create permission) |
enableDropzone |
boolean |
true |
Allow dropping files onto the field |
enableEdit |
boolean |
true |
Thumbnail click opens the image doc in a drawer |
gridCellMinWidth |
number |
140 |
Minimum grid cell width in px |
overrides |
Partial<UploadField> |
— | Escape hatch, merged last |
Data model & migration
The field stores (string \| number)[] — an array of media document IDs —
identical to both a native upload + hasMany: true field and a
relationship + hasMany: true field pointing at the same collection. If you
are migrating from either, no data migration is needed; only the generated
TypeScript types change shape slightly (relationship → upload semantics).
Per-image captions/credits belong on the image document itself (where they are reusable and localizable), not on the gallery. If you need per-usage metadata (same image, different caption per gallery), that requires an array-of-objects data model — see the roadmap.
Roadmap
- Per-usage metadata variant (
arrayof{ image, caption, … }) behind the same grid UX - Bulk sub-selection actions (select N images → remove)
- Sort helpers (by filename / date, reverse)
- List-view
Cellcomponent (stacked thumbnails + count)
Development
npm run dev # tsdown --watch
npm test # vitest unit tests for src/lib
npm run build # tsdown → dist/
just publish # release-it → private registry
Dependencies
Dependencies
| ID | Version |
|---|---|
| qs-esm | ^8.0.1 |
Development dependencies
| ID | Version |
|---|---|
| @payloadcms/ui | ^3.86.0 |
| @types/react | ^19.2.17 |
| payload | ^3.86.0 |
| react | ^19.2.7 |
| react-dom | ^19.2.7 |
| release-it | ^21.0.0 |
| tsdown | ^0.22.7 |
| typescript | ^7.0.2 |
| vitest | ^4.1.10 |
Peer dependencies
| ID | Version |
|---|---|
| @payloadcms/ui | >=3 |
| payload | >=3 |
| react | >=18 |