From e670a0771562e1d07a37ca7a24a502223e8e2aed Mon Sep 17 00:00:00 2001 From: Vadym Samoilenko Date: Fri, 15 May 2026 15:41:45 +0100 Subject: [PATCH] vault backup: 2026-05-15 15:41:45 --- raw/{ => _processed}/fields__ui.md | 0 raw/{ => _processed}/fields__upload.md | 0 wiki/_master-index.md | 2 +- wiki/payloadcms/_index.md | 1 + wiki/payloadcms/fields-upload.md | 100 +++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 1 deletion(-) rename raw/{ => _processed}/fields__ui.md (100%) rename raw/{ => _processed}/fields__upload.md (100%) create mode 100644 wiki/payloadcms/fields-upload.md diff --git a/raw/fields__ui.md b/raw/_processed/fields__ui.md similarity index 100% rename from raw/fields__ui.md rename to raw/_processed/fields__ui.md diff --git a/raw/fields__upload.md b/raw/_processed/fields__upload.md similarity index 100% rename from raw/fields__upload.md rename to raw/_processed/fields__upload.md diff --git a/wiki/_master-index.md b/wiki/_master-index.md index 631f9b1..8d993d3 100644 --- a/wiki/_master-index.md +++ b/wiki/_master-index.md @@ -35,7 +35,7 @@ This 3-hop pattern works for hundreds of articles without vector search. | [[wiki/reports/_index\|reports/]] | Weekly and monthly summaries — generate: `uv run python scripts/report-generator.py --weekly` | 1 | | [[wiki/infrastructure/_index\|infrastructure/]] | Server inventory: all 10 SSH hosts — optical, optical-dev, optical-prod, baic, librechat, modocmms, box-cli, aimpress, pve | 12 | | [[wiki/testing/_index\|testing/]] | Web app testing: functional, performance, security, UI types; TDD/BDD/Agile methodologies; Selenium/Cypress/Playwright/JMeter/OWASP ZAP tools | 1 | -| [[wiki/payloadcms/_index\|payloadcms/]] | Full Payload CMS reference — getting started, config, database (Postgres/MongoDB/SQLite), all 22 field types, access control, hooks, authentication (cookies, JWT, API keys, custom strategies, token data), admin UI, custom components, Lexical rich text, live preview, versions/drafts, Local/REST/GraphQL APIs, queries, plugins, jobs queue, upload, ecommerce, production deploy, TypeScript, migration guides, i18n, localization | 84 | +| [[wiki/payloadcms/_index\|payloadcms/]] | Full Payload CMS reference — getting started, config, database (Postgres/MongoDB/SQLite), all 22 field types, access control, hooks, authentication (cookies, JWT, API keys, custom strategies, token data), admin UI, custom components, Lexical rich text, live preview, versions/drafts, Local/REST/GraphQL APIs, queries, plugins, jobs queue, upload, ecommerce, production deploy, TypeScript, migration guides, i18n, localization | 85 | | [[wiki/shared-patterns/_index\|shared-patterns/]] | Oliver Agency standard library patterns: httpx, structlog, pydantic-settings, alembic — reuse before writing from scratch | 4 | | [[wiki/mistakes/_index\|mistakes/]] | Anti-patterns extracted from sessions — per-stack running lists (fastapi, react, docker, postgres, general) — injected at session start | 5 | diff --git a/wiki/payloadcms/_index.md b/wiki/payloadcms/_index.md index df994dc..6b72c9a 100644 --- a/wiki/payloadcms/_index.md +++ b/wiki/payloadcms/_index.md @@ -85,3 +85,4 @@ | [[wiki/payloadcms/fields-text\|Text Field]] | Plain string field — `hasMany` array mode, built-in Slug Field (`slugField()`), unique index, saveToJWT, virtual, localized, RTL admin | raw/fields__text.md | 2026-05-15 | | [[wiki/payloadcms/fields-textarea\|Textarea Field]] | Multiline string field — identical to Text but with larger input; `rows` (default 2), RTL, no `hasMany`, same validation/virtual/JWT options | raw/fields__textarea.md | 2026-05-15 | | [[wiki/payloadcms/fields-ui\|UI Field]] | Presentational-only field — inject arbitrary React components into Admin Panel Edit/List views; no data stored; use for action buttons, context text, custom controls | raw/fields__ui.md | 2026-05-15 | +| [[wiki/payloadcms/fields-upload\|Upload Field]] | Reference upload-enabled Collection docs as thumbnail — hasMany, polymorphic multi-collection, filterOptions, bi-directional via Join | raw/fields__upload.md | 2026-05-15 | diff --git a/wiki/payloadcms/fields-upload.md b/wiki/payloadcms/fields-upload.md new file mode 100644 index 0000000..2a2a2d7 --- /dev/null +++ b/wiki/payloadcms/fields-upload.md @@ -0,0 +1,100 @@ +--- +title: "Upload Field" +aliases: [upload-field, payload-upload, media-field] +tags: [payloadcms, fields, upload, media, relationships] +sources: [raw/fields__upload.md] +created: 2026-05-15 +updated: 2026-05-15 +--- + +The Upload Field references a document from an upload-enabled Collection, rendered as a thumbnail in the Admin Panel. It is a typed relationship to a media/file Collection. + +## Basic Config + +```ts +import type { Field } from 'payload' + +export const MyUploadField: Field = { + type: 'upload', + name: 'backgroundImage', // required + relationTo: 'media', // required — must be an upload-enabled collection + required: true, +} +``` + +> **Prerequisite:** target Collection must have `upload: true` (or an upload config object) in its [[wiki/payloadcms/collection-config|Collection Config]]. + +## Key Config Options + +| Option | Description | +|--------|-------------| +| `relationTo` | Single slug or array of slugs (polymorphic). **Must** point to upload collection(s). | +| `hasMany` | `true` → accepts multiple uploads | +| `minRows` / `maxRows` | Validation bounds when `hasMany: true` | +| `filterOptions` | `Where` query or function — limits selectable files in UI and validates on save | +| `displayPreview` | Override Collection-level preview thumbnail setting | +| `maxDepth` | Max auto-populate depth for related document | +| `unique` | DB-level unique index | +| `localized` | Per-locale upload reference | +| `virtual` | Skip DB storage or link to a relationship path | +| `saveToJWT` | Include in JWT if field is top-level in an auth Collection | + +## Filtering Upload Options + +Restrict selectable files dynamically: + +```ts +// Static Where query — only images +const imageField = { + name: 'image', + type: 'upload', + relationTo: 'media', + filterOptions: { + mimeType: { contains: 'image' }, + }, +} +``` + +As a function (receives `{ relationTo, data, siblingData, id, user, req }`): + +```ts +filterOptions: ({ user }) => { + if (user.role === 'admin') return true // no filter + return { uploadedBy: { equals: user.id } } +} +``` + +> **Gotcha:** If you combine `filterOptions` with a custom `validate` function, call the default upload field validator from `payload/shared` inside your function — otherwise `filterOptions` won't be validated by the API. + +## Polymorphic Uploads + +Reference multiple upload collections: + +```ts +{ + name: 'media', + type: 'upload', + relationTo: ['images', 'documents', 'videos'], + hasMany: true, // optional — allow multiple selections +} +``` + +When polymorphic, the stored value is `{ relationTo: 'images', value: '' }` (same shape as polymorphic [[wiki/payloadcms/fields-relationship|Relationship Field]]). + +## Bi-directional Relationships + +The upload field alone is a one-way reference. To view from the media document which other documents use it, add a [[wiki/payloadcms/fields-join|Join Field]] on the upload Collection pointing back. + +## Key Takeaways + +- `relationTo` is **required** and must point to an upload-enabled Collection — a plain Collection won't work +- Use `hasMany: true` for galleries / multi-file selectors; pair with `minRows`/`maxRows` for validation +- `filterOptions` filters both UI picker and API validation — mix with `where` queries from [[wiki/payloadcms/queries|Queries]] +- Combining `filterOptions` + custom `validate` requires calling the built-in validator from `payload/shared` +- Polymorphic: pass an array to `relationTo` — stored value gets a `{ relationTo, value }` wrapper +- For reverse lookup (where is this media used?), use [[wiki/payloadcms/fields-join|Join Field]] on the media Collection +- `displayPreview` can be overridden per-field, independent of the Collection setting + +## Sources + +- `raw/fields__upload.md` — Payload CMS official docs, Upload Field