Skip to content

fix(richtext-lexical,ui): make uploadNode default to alt user-defined values.#15097

Merged
GermanJablo merged 1 commit intomainfrom
14780/upload-alt
Jan 6, 2026
Merged

fix(richtext-lexical,ui): make uploadNode default to alt user-defined values.#15097
GermanJablo merged 1 commit intomainfrom
14780/upload-alt

Conversation

@GermanJablo
Copy link
Contributor

Fix #14780

There are 2 ways a user can add fields to UploadNode within richtext:

// 1. Media Collection
export const Media: CollectionConfig = {
  slug: 'media',
  fields: [
    { name: 'alt', type: 'text' }  // ← Default alt for the image
  ],
  upload: true,
}

// 2. UploadFeature in Lexical (Per-instance override)
UploadFeature({
  collections: {
    media: {
      fields: [
        { name: 'alt', type: 'text' }  // ← Context-specific alt per usage
      ]
    }
  }
})

This PR updates all upload converters (JSX, HTML async/sync, Slate, and UI components) to intelligently resolve the alt attribute with the following priority:

  1. UploadFeature fields.alt - Context-specific override
  2. Media collection alt - Default from document
  3. Empty string - Accessibility fallback (instead of using filename)

This prevents users from having to manually override converters to achieve proper alt text handling.

Implementation note: Type assertions are required since both alt fields are user-defined and don't exist in the core types at compile time.

@GermanJablo GermanJablo changed the title fix(richtext, ui): make uploadNode default to alt user-defined values. fix(richtext,ui): make uploadNode default to alt user-defined values. Jan 6, 2026
@GermanJablo GermanJablo changed the title fix(richtext,ui): make uploadNode default to alt user-defined values. fix(richtext-lexical,ui): make uploadNode default to alt user-defined values. Jan 6, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Jan 6, 2026

📦 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 934.87 KB ⚠️ +42 B (+0.0%)
packages/payload/meta_index.json esbuild/index.js 1.23 MB ✅ No change
packages/payload/meta_shared.json esbuild/exports/shared.js 164.31 KB ✅ No change
packages/richtext-lexical/meta_client.json esbuild/exports/client_optimized/index.js 281.24 KB ⚠️ +9 B (+0.0%)
packages/ui/meta_client.json esbuild/exports/client_optimized/index.js 1.16 MB ⚠️ +25 B (+0.0%)
packages/ui/meta_shared.json esbuild/exports/shared_optimized/index.js 15.65 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}{ ████████████████████▌ }}}$ 82.4%, 766.76 KB
dist/views/Version ${{\color{Goldenrod}{ █▎ }}}$ 5.4%, 50.31 KB
dist/views/Dashboard ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 16.48 KB
dist/views/Document ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 15.42 KB
dist/views/List ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 11.27 KB
dist/views/Root ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 9.03 KB
dist/views/API ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.03 KB
dist/views/Versions ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 5.98 KB
dist/elements/Nav ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 5.53 KB
dist/views/Account ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 5.46 KB
dist/elements/DocumentHeader ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 4.81 KB
dist/views/Login ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 4.41 KB
dist/views/ForgotPassword ${{\color{Goldenrod}{ }}}$ 0.3%, 3.09 KB
dist/layouts/Root ${{\color{Goldenrod}{ }}}$ 0.3%, 2.91 KB
dist/views/CreateFirstUser ${{\color{Goldenrod}{ }}}$ 0.3%, 2.81 KB
dist/templates/Default ${{\color{Goldenrod}{ }}}$ 0.3%, 2.63 KB
dist/views/BrowseByFolder ${{\color{Goldenrod}{ }}}$ 0.3%, 2.56 KB
dist/views/CollectionFolders ${{\color{Goldenrod}{ }}}$ 0.3%, 2.44 KB
dist/views/ResetPassword ${{\color{Goldenrod}{ }}}$ 0.3%, 2.40 KB
dist/views/Logout ${{\color{Goldenrod}{ }}}$ 0.2%, 1.94 KB
(other) ${{\color{Goldenrod}{ ████▍ }}}$ 17.6%, 163.43 KB

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

Path Size
../../node_modules ${{\color{Goldenrod}{ █████████████████▏ }}}$ 68.6%, 842.04 KB
dist/fields/hooks ${{\color{Goldenrod}{ ▉ }}}$ 3.5%, 43.34 KB
dist/collections/operations ${{\color{Goldenrod}{ ▊ }}}$ 3.0%, 36.43 KB
dist/auth/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 15.21 KB
dist/globals/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 11.98 KB
dist/fields/config ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 11.95 KB
dist/utilities/configToJSONSchema.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 11.83 KB
dist/queues/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 11.76 KB
dist/fields/validations.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 10.21 KB
dist/bin/generateImportMap ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.53 KB
dist/collections/config ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.90 KB
dist/uploads/fetchAPI-multipart ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.74 KB
dist/index.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.65 KB
dist/database/migrations ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.50 KB
dist/config/orderable ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 6.27 KB
dist/collections/endpoints ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 6.00 KB
dist/config/sanitize.js ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 5.70 KB
dist/auth/strategies ${{\color{Goldenrod}{ }}}$ 0.4%, 5.50 KB
dist/auth/endpoints ${{\color{Goldenrod}{ }}}$ 0.4%, 5.42 KB
dist/utilities/telemetry ${{\color{Goldenrod}{ }}}$ 0.4%, 5.31 KB
(other) ${{\color{Goldenrod}{ ███████▊ }}}$ 31.4%, 385.37 KB

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

Path Size
../../node_modules ${{\color{Goldenrod}{ ███████████████████▋ }}}$ 78.9%, 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.51 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/getVersionsConfig.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 895 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.3%, 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
(other) ${{\color{Goldenrod}{ █████▎ }}}$ 21.1%, 33.88 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%, 35.02 KB
dist/lexical/plugins ${{\color{Goldenrod}{ ██▉ }}}$ 11.5%, 32.00 KB
dist/lexical/ui ${{\color{Goldenrod}{ ██▏ }}}$ 8.8%, 24.36 KB
dist/features/experimental_table ${{\color{Goldenrod}{ ██▏ }}}$ 8.5%, 23.70 KB
dist/packages/@lexical ${{\color{Goldenrod}{ █▋ }}}$ 6.8%, 18.99 KB
dist/features/link ${{\color{Goldenrod}{ █▋ }}}$ 6.5%, 18.11 KB
dist/features/toolbars ${{\color{Goldenrod}{ █▌ }}}$ 6.4%, 17.75 KB
dist/features/upload ${{\color{Goldenrod}{ █▎ }}}$ 5.0%, 13.77 KB
dist/features/textState ${{\color{Goldenrod}{ █ }}}$ 4.0%, 11.08 KB
dist/features/relationship ${{\color{Goldenrod}{ ▊ }}}$ 3.2%, 9.03 KB
dist/lexical/utils ${{\color{Goldenrod}{ ▊ }}}$ 3.0%, 8.22 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.05 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.2%, 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%, 242.96 KB

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

Path Size
../../node_modules ${{\color{Goldenrod}{ ████████████▍ }}}$ 49.6%, 572.86 KB
dist/elements/FolderView ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 29.30 KB
dist/elements/BulkUpload ${{\color{Goldenrod}{ ▌ }}}$ 2.4%, 27.19 KB
dist/elements/WhereBuilder ${{\color{Goldenrod}{ ▍ }}}$ 1.5%, 16.91 KB
dist/views/Edit ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 16.39 KB
dist/fields/Relationship ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 15.79 KB
dist/elements/Table ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 15.48 KB
dist/forms/Form ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 15.25 KB
dist/fields/Upload ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 14.12 KB
dist/fields/Blocks ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 13.70 KB
dist/elements/QueryPresets ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 10.33 KB
dist/elements/PublishButton ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 8.84 KB
dist/providers/Folders ${{\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.90 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.38 KB
dist/elements/ReactSelect ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.31 KB
dist/views/List ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.99 KB
(other) ${{\color{Goldenrod}{ ████████████▌ }}}$ 50.4%, 581.80 KB

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

Path Size
dist/graphics/Logo ${{\color{Goldenrod}{ █████▏ }}}$ 20.8%, 3.12 KB
../../node_modules ${{\color{Goldenrod}{ ████▍ }}}$ 17.7%, 2.65 KB
dist/graphics/Icon ${{\color{Goldenrod}{ ██▌ }}}$ 10.2%, 1.52 KB
dist/utilities/formatDocTitle ${{\color{Goldenrod}{ ██▏ }}}$ 8.8%, 1.32 KB
dist/providers/TableColumns ${{\color{Goldenrod}{ █▍ }}}$ 5.7%, 862 B
dist/utilities/groupNavItems.js ${{\color{Goldenrod}{ █▎ }}}$ 5.4%, 812 B
dist/utilities/api.js ${{\color{Goldenrod}{ █▎ }}}$ 5.0%, 756 B
dist/utilities/getGlobalData.js ${{\color{Goldenrod}{ ▉ }}}$ 3.5%, 528 B
dist/elements/Translation ${{\color{Goldenrod}{ ▊ }}}$ 3.3%, 493 B
dist/utilities/handleTakeOver.js ${{\color{Goldenrod}{ ▋ }}}$ 2.9%, 440 B
dist/elements/withMergedProps ${{\color{Goldenrod}{ ▌ }}}$ 2.3%, 339 B
dist/utilities/getVisibleEntities.js ${{\color{Goldenrod}{ ▌ }}}$ 2.2%, 329 B
dist/utilities/getNavGroups.js ${{\color{Goldenrod}{ ▌ }}}$ 2.0%, 301 B
dist/elements/WithServerSideProps ${{\color{Goldenrod}{ ▍ }}}$ 1.5%, 232 B
dist/utilities/handleGoBack.js ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 180 B
dist/fields/mergeFieldStyles.js ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 159 B
dist/utilities/handleBackToDashboard.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 152 B
dist/forms/Form ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 147 B
dist/utilities/abortAndIgnore.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 146 B
dist/utilities/hasSavePermission.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 136 B
(other) ${{\color{Goldenrod}{ ███████████████████▊ }}}$ 79.2%, 11.87 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.

@GermanJablo GermanJablo merged commit 3290b04 into main Jan 6, 2026
581 of 597 checks passed
@GermanJablo GermanJablo deleted the 14780/upload-alt branch January 6, 2026 21:22
teastudiopl pushed a commit to teastudiopl/payload that referenced this pull request Jan 8, 2026
…ed values. (payloadcms#15097)

Fix payloadcms#14780

There are 2 ways a user can add fields to UploadNode within richtext:
```ts
// 1. Media Collection
export const Media: CollectionConfig = {
  slug: 'media',
  fields: [
    { name: 'alt', type: 'text' }  // ← Default alt for the image
  ],
  upload: true,
}

// 2. UploadFeature in Lexical (Per-instance override)
UploadFeature({
  collections: {
    media: {
      fields: [
        { name: 'alt', type: 'text' }  // ← Context-specific alt per usage
      ]
    }
  }
})
```
This PR updates all upload converters (JSX, HTML async/sync, Slate, and
UI components) to intelligently resolve the `alt` attribute with the
following priority:

1. **UploadFeature `fields.alt`** - Context-specific override
2. **Media collection `alt`** - Default from document
3. **Empty string** - Accessibility fallback (instead of using filename)

This prevents users from having to manually override converters to
achieve proper alt text handling.

**Implementation note:** Type assertions are required since both `alt`
fields are user-defined and don't exist in the core types at compile
time.
@github-actions
Copy link
Contributor

🚀 This is included in version v3.71.0

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.

A11y: UploadJSXConverter uses filename instead of alt field for image alt attribute

2 participants