Skip to content

Conversation

@paulpopus
Copy link
Contributor

@paulpopus paulpopus commented Oct 27, 2025

This PR adds support for polymorphic uploads similar to polymorphic relationships. Closes #13912

It works with bulk upload as well and in the bulk uploader you can choose which collection the files are uploaded into.

You can enable this by adding an array of slugs into relationTo:

import type { CollectionConfig } from 'payload'

export const ExampleCollection: CollectionConfig = {
  slug: 'example-collection',
  fields: [
    {
      name: 'media',
      type: 'upload',
      relationTo: ['images', 'documents', 'videos'], // references multiple upload collections
    },
  ],
}

It can also be combined with hasMany:

import type { CollectionConfig } from 'payload'

export const ExampleCollection: CollectionConfig = {
  slug: 'example-collection',
  fields: [
    {
      name: 'media',
      type: 'upload',
      relationTo: ['images', 'documents', 'videos'], // references multiple upload collections
      hasMany: true, // allows multiple uploads
    },
  ],
}

Known issue: #12014
Filtering by polymorphic relationships is currently broken and I'll work on that in a separate PR.

@JarrodMFlesch
Copy link
Contributor

I think the where builder will need to be adjusted to handle (by constraining options) poly upload fields. Similar to how the relationship field does here: https://github.com/payloadcms/payload/blob/main/packages/ui/src/elements/WhereBuilder/field-types.tsx

@github-actions
Copy link
Contributor

github-actions bot commented Oct 27, 2025

📦 esbuild Bundle Analysis for payload

This analysis was generated by esbuild-bundle-analyzer. 🤖

Meta File Out File Size (raw) Note
packages/next/meta_index.json esbuild/index.js 756.04 KB ⚠️ +506 B (+0.1%)
packages/payload/meta_index.json esbuild/index.js 1.23 MB ✅ No change
packages/payload/meta_shared.json esbuild/exports/shared.js 163.07 KB ✅ No change
packages/richtext-lexical/meta_client.json esbuild/exports/client_optimized/index.js 279.48 KB ✅ No change
packages/ui/meta_client.json esbuild/exports/client_optimized/index.js 1.15 MB ⚠️ +2.41 KB (+0.2%)
packages/ui/meta_shared.json esbuild/exports/shared_optimized/index.js 14.39 KB ✅ No change
Largest paths These visualization shows top 20 largest paths in the bundle.

Meta file: packages/next/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ████████████████████ }}}$ 80.1%, 601.84 KB
dist/views/Version ${{\color{Goldenrod}{ █▋ }}}$ 6.7%, 50.19 KB
dist/views/Document ${{\color{Goldenrod}{ ▌ }}}$ 2.1%, 15.43 KB
dist/views/List ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 10.46 KB
dist/views/Root ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 8.70 KB
dist/views/API ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 5.98 KB
dist/views/Versions ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 5.96 KB
dist/elements/Nav ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 5.53 KB
dist/views/Account ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 5.32 KB
dist/elements/DocumentHeader ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 4.81 KB
dist/views/Login ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 4.39 KB
dist/views/Dashboard ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 3.69 KB
dist/views/ForgotPassword ${{\color{Goldenrod}{ }}}$ 0.4%, 3.09 KB
dist/layouts/Root ${{\color{Goldenrod}{ }}}$ 0.4%, 2.91 KB
dist/templates/Default ${{\color{Goldenrod}{ }}}$ 0.4%, 2.83 KB
dist/views/CreateFirstUser ${{\color{Goldenrod}{ }}}$ 0.4%, 2.76 KB
dist/views/BrowseByFolder ${{\color{Goldenrod}{ }}}$ 0.3%, 2.60 KB
dist/views/CollectionFolders ${{\color{Goldenrod}{ }}}$ 0.3%, 2.46 KB
dist/views/ResetPassword ${{\color{Goldenrod}{ }}}$ 0.3%, 2.41 KB
dist/views/Logout ${{\color{Goldenrod}{ }}}$ 0.3%, 1.91 KB
(other) ${{\color{Goldenrod}{ ████▉ }}}$ 19.9%, 149.52 KB

Meta file: packages/payload/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ █████████████████▏ }}}$ 68.9%, 841.07 KB
dist/fields/hooks ${{\color{Goldenrod}{ ▊ }}}$ 3.4%, 41.96 KB
dist/collections/operations ${{\color{Goldenrod}{ ▊ }}}$ 3.0%, 37.12 KB
dist/auth/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 15.26 KB
dist/queues/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 12.04 KB
dist/globals/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 11.91 KB
dist/fields/config ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 11.80 KB
dist/utilities/configToJSONSchema.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 11.50 KB
dist/fields/validations.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 10.21 KB
dist/bin/generateImportMap ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.38 KB
dist/database/migrations ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.07 KB
dist/collections/config ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.88 KB
dist/uploads/fetchAPI-multipart ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.74 KB
dist/index.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.59 KB
dist/collections/endpoints ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.94 KB
dist/config/orderable ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 6.27 KB
dist/config/sanitize.js ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 5.53 KB
dist/auth/strategies ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 5.50 KB
dist/auth/endpoints ${{\color{Goldenrod}{ }}}$ 0.4%, 5.41 KB
dist/utilities/telemetry ${{\color{Goldenrod}{ }}}$ 0.4%, 5.31 KB
(other) ${{\color{Goldenrod}{ ███████▊ }}}$ 31.1%, 379.14 KB

Meta file: packages/payload/meta_shared.json, Out file: esbuild/exports/shared.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ███████████████████▉ }}}$ 79.5%, 126.93 KB
dist/fields/validations.js ${{\color{Goldenrod}{ █▌ }}}$ 6.4%, 10.21 KB
dist/fields/baseFields ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 2.79 KB
dist/utilities/deepCopyObject.js ${{\color{Goldenrod}{ ▍ }}}$ 1.6%, 2.48 KB
dist/auth/cookies.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 1.55 KB
dist/utilities/flattenTopLevelFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 1.42 KB
dist/fields/config ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 1.28 KB
dist/utilities/flattenAllFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 943 B
dist/folders/utils ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 916 B
dist/utilities/unflatten.js ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 779 B
dist/utilities/sanitizeUserDataForEmail.js ${{\color{Goldenrod}{ }}}$ 0.4%, 713 B
dist/utilities/getFieldPermissions.js ${{\color{Goldenrod}{ }}}$ 0.4%, 651 B
dist/collections/config ${{\color{Goldenrod}{ }}}$ 0.4%, 570 B
dist/bin/generateImportMap ${{\color{Goldenrod}{ }}}$ 0.4%, 561 B
dist/auth/sessions.js ${{\color{Goldenrod}{ }}}$ 0.3%, 525 B
dist/utilities/getSafeRedirect.js ${{\color{Goldenrod}{ }}}$ 0.3%, 423 B
dist/utilities/deepMerge.js ${{\color{Goldenrod}{ }}}$ 0.3%, 413 B
dist/utilities/formatLabels.js ${{\color{Goldenrod}{ }}}$ 0.2%, 380 B
dist/utilities/appendUploadSelectFields.js ${{\color{Goldenrod}{ }}}$ 0.2%, 360 B
dist/utilities/transformColumnPreferences.js ${{\color{Goldenrod}{ }}}$ 0.2%, 348 B
(other) ${{\color{Goldenrod}{ █████▏ }}}$ 20.5%, 32.80 KB

Meta file: packages/richtext-lexical/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
dist/features/blocks ${{\color{Goldenrod}{ ███▏ }}}$ 12.6%, 34.80 KB
dist/lexical/plugins ${{\color{Goldenrod}{ ██▉ }}}$ 11.5%, 31.68 KB
dist/lexical/ui ${{\color{Goldenrod}{ ██▏ }}}$ 8.8%, 24.36 KB
dist/features/experimental_table ${{\color{Goldenrod}{ ██▏ }}}$ 8.6%, 23.70 KB
dist/packages/@lexical ${{\color{Goldenrod}{ █▋ }}}$ 6.9%, 18.99 KB
dist/features/link ${{\color{Goldenrod}{ █▋ }}}$ 6.5%, 18.04 KB
dist/features/toolbars ${{\color{Goldenrod}{ █▌ }}}$ 6.4%, 17.75 KB
dist/features/upload ${{\color{Goldenrod}{ █▎ }}}$ 5.0%, 13.69 KB
dist/features/textState ${{\color{Goldenrod}{ █ }}}$ 4.0%, 11.08 KB
dist/features/relationship ${{\color{Goldenrod}{ ▊ }}}$ 3.2%, 8.96 KB
dist/lexical/utils ${{\color{Goldenrod}{ ▋ }}}$ 2.9%, 8.08 KB
dist/features/debug ${{\color{Goldenrod}{ ▋ }}}$ 2.7%, 7.39 KB
dist/utilities/fieldsDrawer ${{\color{Goldenrod}{ ▋ }}}$ 2.6%, 7.12 KB
dist/features/converters ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 7.04 KB
dist/lexical/config ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 5.08 KB
dist/features/lists ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 5.00 KB
dist/features/format ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 3.46 KB
dist/lexical/LexicalEditor.js ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 3.17 KB
dist/lexical/theme ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 2.62 KB
dist/features/indent ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 2.50 KB
(other) ${{\color{Goldenrod}{ █████████████████████▊ }}}$ 87.4%, 241.42 KB

Meta file: packages/ui/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ████████████▌ }}}$ 50.1%, 572.84 KB
dist/elements/FolderView ${{\color{Goldenrod}{ ▋ }}}$ 2.6%, 29.18 KB
dist/elements/BulkUpload ${{\color{Goldenrod}{ ▌ }}}$ 2.4%, 26.93 KB
dist/elements/WhereBuilder ${{\color{Goldenrod}{ ▍ }}}$ 1.5%, 16.84 KB
dist/views/Edit ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 16.03 KB
dist/elements/Table ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 15.44 KB
dist/fields/Relationship ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 15.40 KB
dist/forms/Form ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 15.12 KB
dist/fields/Upload ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 13.75 KB
dist/fields/Blocks ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 12.89 KB
dist/elements/PublishButton ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 8.75 KB
dist/providers/Folders ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.49 KB
dist/elements/QueryPresets ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.46 KB
dist/elements/LivePreview ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.38 KB
dist/elements/ListHeader ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 7.83 KB
dist/elements/HTMLDiff ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 7.81 KB
dist/fields/Array ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 7.55 KB
dist/views/CollectionFolder ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.37 KB
dist/views/List ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.96 KB
dist/elements/ReactSelect ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.92 KB
(other) ${{\color{Goldenrod}{ ████████████▍ }}}$ 49.9%, 570.79 KB

Meta file: packages/ui/meta_shared.json, Out file: esbuild/exports/shared_optimized/index.js

Path Size
dist/graphics/Logo ${{\color{Goldenrod}{ █████▋ }}}$ 22.6%, 3.12 KB
../../node_modules ${{\color{Goldenrod}{ ████▊ }}}$ 19.2%, 2.65 KB
dist/graphics/Icon ${{\color{Goldenrod}{ ██▊ }}}$ 11.0%, 1.52 KB
dist/utilities/formatDocTitle ${{\color{Goldenrod}{ ██▍ }}}$ 9.6%, 1.32 KB
dist/providers/TableColumns ${{\color{Goldenrod}{ █▌ }}}$ 6.2%, 862 B
dist/utilities/groupNavItems.js ${{\color{Goldenrod}{ █▍ }}}$ 5.9%, 814 B
dist/utilities/api.js ${{\color{Goldenrod}{ █▍ }}}$ 5.5%, 756 B
dist/elements/Translation ${{\color{Goldenrod}{ ▉ }}}$ 3.6%, 493 B
dist/utilities/handleTakeOver.js ${{\color{Goldenrod}{ ▊ }}}$ 3.2%, 440 B
dist/elements/withMergedProps ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 339 B
dist/elements/WithServerSideProps ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 232 B
dist/utilities/handleGoBack.js ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 168 B
dist/fields/mergeFieldStyles.js ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 159 B
dist/forms/Form ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 147 B
dist/utilities/abortAndIgnore.js ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 146 B
dist/utilities/hasSavePermission.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 136 B
dist/utilities/handleBackToDashboard.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 129 B
dist/utilities/findLocaleFromCode.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 84 B
dist/utilities/sanitizeID.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 77 B
dist/utilities/isEditing.js ${{\color{Goldenrod}{ }}}$ 0.4%, 59 B
(other) ${{\color{Goldenrod}{ ███████████████████▎ }}}$ 77.4%, 10.68 KB
Details

Next to the size is how much the size has increased or decreased compared with the base branch of this PR.

  • ‼️: Size increased by 20% or more. Special attention should be given to this.
  • ⚠️: Size increased in acceptable range (lower than 20%).
  • ✅: No change or even downsized.
  • 🗑️: The out file is deleted: not found in base branch.
  • 🆕: The out file is newly found: will be added to base branch.

@paulpopus paulpopus requested a review from denolfe as a code owner October 27, 2025 17:44
@DanRibbens DanRibbens self-requested a review November 5, 2025 18:37
@DanRibbens DanRibbens requested a review from Copilot November 5, 2025 21:59
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for polymorphic uploads to Payload CMS, allowing upload fields to reference multiple upload collections. This mirrors the existing polymorphic relationship functionality and works with both single and multiple file uploads via the hasMany property.

Key changes:

  • Upload fields can now accept an array of collection slugs in relationTo instead of just a single slug
  • Bulk uploader UI updated to support collection selection for polymorphic uploads
  • Type definitions updated to handle polymorphic upload data structures across the codebase

Reviewed Changes

Copilot reviewed 23 out of 24 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test/uploads/payload-types.ts Updated type definitions from numeric to string IDs for upload collections
test/fields/seed.ts Modified seed data to use polymorphic upload format with relationTo and value structure
test/fields/payload-types.ts Added polymorphic type definitions for multi-upload fields
test/fields/collections/UploadPoly/e2e.spec.ts New E2E test for single polymorphic upload functionality
test/fields/collections/UploadMultiPoly/e2e.spec.ts New E2E test for multi-file polymorphic upload functionality
test/fields/collections/UploadMulti/index.ts Updated upload field to support multiple collections
packages/ui/src/fields/Upload/types.ts Added type definitions for polymorphic upload values and deprecated signatures
packages/ui/src/fields/Upload/index.tsx Added logic to transform values for polymorphic uploads
packages/ui/src/fields/Upload/RelationshipContent/index.tsx Added collection slug pill display for polymorphic uploads
packages/ui/src/fields/Upload/Input.tsx Major refactor to handle polymorphic upload data fetching and state management
packages/ui/src/fields/Upload/HasOne/index.tsx Added support for displaying collection slug
packages/ui/src/fields/Upload/HasMany/index.tsx Added support for displaying collection slug
packages/ui/src/elements/WhereBuilder/field-types.tsx Changed upload field filter component from Text to Relationship
packages/ui/src/elements/WhereBuilder/Condition/Relationship/types.ts Extended relationship filter to support upload fields
packages/ui/src/elements/WhereBuilder/Condition/Relationship/index.tsx Updated to handle both relationship and upload field types
packages/ui/src/elements/WhereBuilder/Condition/DefaultFilter/index.tsx Added case for upload field filtering
packages/ui/src/elements/ReactSelect/MultiValue/index.tsx Added null safety for option value access
packages/plugin-seo/src/fields/MetaImage/index.ts Added type assertion for upload field
packages/payload/src/fields/config/types.ts Enabled polymorphic upload field types
packages/next/src/views/Version/RenderFieldsToDiff/fields/Upload/index.tsx Updated version diff rendering to handle polymorphic uploads
packages/graphql/src/schema/fieldToSchemaMap.ts Updated GraphQL resolvers to handle polymorphic uploads and filter non-GraphQL collections
docs/fields/upload.mdx Added documentation for polymorphic uploads feature
.github/workflows/main.yml Added new E2E test collections to CI workflow

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

DanRibbens
DanRibbens previously approved these changes Nov 5, 2025
@DanRibbens
Copy link
Contributor

I tested manually some different configs with localization and versions.drafts enabled and it all seems to be working great. Nice job!

@paulpopus paulpopus enabled auto-merge (squash) November 6, 2025 12:23
@paulpopus paulpopus merged commit df40d0e into main Nov 6, 2025
181 of 184 checks passed
@paulpopus paulpopus deleted the feat/add-support-for-polymorphic-uploads branch November 6, 2025 14:40
@github-actions
Copy link
Contributor

github-actions bot commented Nov 7, 2025

🚀 This is included in version v3.63.0

zubricks pushed a commit that referenced this pull request Nov 10, 2025
This PR adds support for polymorphic uploads similar to polymorphic
relationships. Closes
#13912

It works with bulk upload as well and in the bulk uploader you can
choose which collection the files are uploaded into.

You can enable this by adding an array of slugs into `relationTo`:

```ts
import type { CollectionConfig } from 'payload'

export const ExampleCollection: CollectionConfig = {
  slug: 'example-collection',
  fields: [
    {
      name: 'media',
      type: 'upload',
      relationTo: ['images', 'documents', 'videos'], // references multiple upload collections
    },
  ],
}
```

It can also be combined with `hasMany`:

```ts
import type { CollectionConfig } from 'payload'

export const ExampleCollection: CollectionConfig = {
  slug: 'example-collection',
  fields: [
    {
      name: 'media',
      type: 'upload',
      relationTo: ['images', 'documents', 'videos'], // references multiple upload collections
      hasMany: true, // allows multiple uploads
    },
  ],
}
```


**Known issue**: #12014
Filtering by polymorphic relationships is currently broken and I'll work
on that in a separate PR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants