Skip to content

feat(ui): preview image edits before saving - #18622

Open
nathanlentz wants to merge 2 commits into
feat/file-transform-state-mainfrom
feat/upload-edit-preview
Open

nathanlentz wants to merge 2 commits into
feat/file-transform-state-mainfrom
feat/upload-edit-preview

Conversation

@nathanlentz

@nathanlentz nathanlentz commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Applying an image edit now updates the Admin preview immediately, before the document is saved.

When you select Apply Changes, the Admin sends the pending edits to a new preview endpoint that applies them to the original image using the transformation API. The endpoint returns temporary image previews without saving the document or storing transformed files in Payload. Selecting Save persists the edits as usual.

To achieve this, I added the following:

  • A new preview endpoint: Accepts unsaved edits or new file bytes, runs permission/content checks then returns an image without persisting the changes anywhere in payload.
  • Transformer mode preview: Runs the existing transformation pipeline against a temp state
  • Admin previous hook: Requests previews after "Apply Changes" is selected, then displays the temporary blob urls for the image and its sizes.

Preview

payload-pr-demo-upload-edit-preview-take-1.mp4

Drag crop

payload-pr-demo-drag-crop-take-2.mp4

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

📦 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 242.21 KB 🆕 Added
packages/payload/meta_index.json esbuild/index.js 1.95 MB 🆕 Added
packages/payload/meta_shared.json esbuild/exports/shared.js 543.02 KB 🆕 Added
packages/richtext-lexical/meta_client.json esbuild/exports/client_optimized/index.js 295.46 KB 🆕 Added
packages/ui/meta_client.json esbuild/exports/client_optimized/index.js 1.32 MB 🆕 Added
packages/ui/meta_shared.json esbuild/exports/shared_optimized/index.js 19.14 KB 🆕 Added
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}{ ████████████████████████▋ }}}$ 98.9%, 237.09 KB
dist/adapters/router.js ${{\color{Goldenrod}{ }}}$ 0.3%, 718 B
dist/adapters/server.js ${{\color{Goldenrod}{ }}}$ 0.2%, 533 B
dist/adapters/layout.js ${{\color{Goldenrod}{ }}}$ 0.2%, 529 B
dist/adapters/views.js ${{\color{Goldenrod}{ }}}$ 0.1%, 324 B
dist/utilities/initAdminContext.js ${{\color{Goldenrod}{ }}}$ 0.1%, 315 B
dist/utilities/selectiveCache.js ${{\color{Goldenrod}{ }}}$ 0.1%, 263 B
dist/esbuildEntry.js ${{\color{Goldenrod}{ }}}$ 0.0%, 0 B

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

Path Size
../../node_modules ${{\color{Goldenrod}{ ████████████████▉ }}}$ 67.6%, 1.32 MB
dist/collections/operations ${{\color{Goldenrod}{ ▉ }}}$ 3.6%, 69.66 KB
dist/fields/hooks ${{\color{Goldenrod}{ ▌ }}}$ 2.4%, 46.57 KB
dist/uploads/fileVersioning ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 22.51 KB
dist/globals/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 19.40 KB
dist/auth/operations ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 17.10 KB
dist/uploads/transformers ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 16.29 KB
dist/utilities/configToJSONSchema.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 16.08 KB
dist/queues/operations ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 14.33 KB
dist/fields/config ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 13.78 KB
dist/collections/config ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 11.96 KB
dist/utilities/telemetry ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 11.93 KB
dist/uploads/generateFileData.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 11.45 KB
dist/collections/endpoints ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 10.72 KB
dist/fields/validations.js ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 10.29 KB
dist/cli/commands ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 9.93 KB
dist/config/orderable ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 8.78 KB
dist/uploads/fetchAPI-multipart ${{\color{Goldenrod}{ }}}$ 0.4%, 8.39 KB
dist/config/sanitize.js ${{\color{Goldenrod}{ }}}$ 0.4%, 8.17 KB
dist/database/migrations ${{\color{Goldenrod}{ }}}$ 0.4%, 8.16 KB
(other) ${{\color{Goldenrod}{ ████████ }}}$ 32.4%, 629.29 KB

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

Path Size
../../node_modules ${{\color{Goldenrod}{ ██████████████████████ }}}$ 88.2%, 474.44 KB
dist/fields/validations.js ${{\color{Goldenrod}{ ▍ }}}$ 1.9%, 10.26 KB
dist/fields/config ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 5.83 KB
dist/utilities/traverseFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 4.34 KB
dist/utilities/deepCopyObject.js ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 3.52 KB
dist/collections/config ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 3.44 KB
dist/config/orderable ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 3.13 KB
dist/fields/baseFields ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 2.79 KB
dist/config/client.js ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 2.58 KB
dist/auth/cookies.js ${{\color{Goldenrod}{ }}}$ 0.3%, 1.55 KB
dist/utilities/flattenTopLevelFields.js ${{\color{Goldenrod}{ }}}$ 0.3%, 1.42 KB
dist/utilities/getVersionsConfig.js ${{\color{Goldenrod}{ }}}$ 0.2%, 1.04 KB
dist/globals/config ${{\color{Goldenrod}{ }}}$ 0.2%, 957 B
dist/uploads/generatePayloadFileURL.js ${{\color{Goldenrod}{ }}}$ 0.2%, 864 B
dist/utilities/unflatten.js ${{\color{Goldenrod}{ }}}$ 0.2%, 850 B
dist/utilities/flattenAllFields.js ${{\color{Goldenrod}{ }}}$ 0.1%, 798 B
dist/errors/ValidationError.js ${{\color{Goldenrod}{ }}}$ 0.1%, 772 B
dist/utilities/sanitizeUserDataForEmail.js ${{\color{Goldenrod}{ }}}$ 0.1%, 713 B
dist/auth/extractJWT.js ${{\color{Goldenrod}{ }}}$ 0.1%, 696 B
dist/utilities/getFieldPermissions.js ${{\color{Goldenrod}{ }}}$ 0.1%, 681 B
(other) ${{\color{Goldenrod}{ ██▉ }}}$ 11.8%, 63.65 KB

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

Path Size
dist/features/blocks ${{\color{Goldenrod}{ ███▋ }}}$ 14.6%, 42.66 KB
dist/lexical/plugins ${{\color{Goldenrod}{ ██▉ }}}$ 11.8%, 34.51 KB
dist/lexical/ui ${{\color{Goldenrod}{ ██▉ }}}$ 11.5%, 33.54 KB
dist/features/table ${{\color{Goldenrod}{ ██▎ }}}$ 9.4%, 27.46 KB
dist/features/link ${{\color{Goldenrod}{ █▌ }}}$ 6.3%, 18.56 KB
dist/features/toolbars ${{\color{Goldenrod}{ █▌ }}}$ 6.3%, 18.41 KB
dist/features/upload ${{\color{Goldenrod}{ █▏ }}}$ 4.8%, 14.03 KB
dist/features/textState ${{\color{Goldenrod}{ ▉ }}}$ 3.8%, 11.08 KB
dist/lexical/utils ${{\color{Goldenrod}{ ▊ }}}$ 3.4%, 10.02 KB
dist/features/relationship ${{\color{Goldenrod}{ ▊ }}}$ 3.2%, 9.32 KB
dist/features/converters ${{\color{Goldenrod}{ ▋ }}}$ 2.9%, 8.41 KB
dist/utilities/fieldsDrawer ${{\color{Goldenrod}{ ▋ }}}$ 2.8%, 8.12 KB
dist/features/debug ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 7.40 KB
dist/lexical/config ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 5.14 KB
dist/features/indent ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 4.19 KB
dist/features/lists ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 3.67 KB
dist/lexical/LexicalEditor.js ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 3.33 KB
dist/features/horizontalRule ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 3.29 KB
dist/features/format ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 3.28 KB
dist/field/Field.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 3.06 KB
(other) ${{\color{Goldenrod}{ █████████████████████▎ }}}$ 85.4%, 249.59 KB

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

Path Size
../../node_modules ${{\color{Goldenrod}{ ███████▎ }}}$ 29.2%, 382.60 KB
dist/elements/Hierarchy ${{\color{Goldenrod}{ ▉ }}}$ 3.8%, 49.34 KB
dist/elements/BulkUpload ${{\color{Goldenrod}{ ▌ }}}$ 2.3%, 30.54 KB
dist/elements/Table ${{\color{Goldenrod}{ ▌ }}}$ 2.2%, 28.86 KB
dist/views/Version ${{\color{Goldenrod}{ ▌ }}}$ 2.1%, 27.71 KB
dist/views/Edit ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 22.15 KB
dist/views/HierarchyList ${{\color{Goldenrod}{ ▍ }}}$ 1.6%, 21.53 KB
dist/views/Dashboard ${{\color{Goldenrod}{ ▍ }}}$ 1.5%, 19.60 KB
dist/elements/WhereBuilder ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 17.65 KB
dist/providers/Auth ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 17.45 KB
dist/fields/Relationship ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 16.23 KB
dist/forms/Form ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 15.99 KB
dist/fields/Upload ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 14.96 KB
dist/fields/Blocks ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 14.91 KB
dist/elements/Popup ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 13.32 KB
dist/views/List ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 13.21 KB
dist/elements/ReactSelect ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 12.98 KB
dist/elements/PublishButton ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 11.99 KB
dist/elements/Upload ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 11.62 KB
dist/elements/QueryPresets ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 10.82 KB
(other) ${{\color{Goldenrod}{ █████████████████▋ }}}$ 70.8%, 928.10 KB

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

Path Size
dist/graphics/Logo ${{\color{Goldenrod}{ ███████▌ }}}$ 30.4%, 5.61 KB
../../node_modules ${{\color{Goldenrod}{ ███▌ }}}$ 14.4%, 2.65 KB
dist/graphics/Icon ${{\color{Goldenrod}{ ██ }}}$ 8.2%, 1.51 KB
dist/utilities/formatDocTitle ${{\color{Goldenrod}{ █▊ }}}$ 7.1%, 1.32 KB
dist/providers/TableColumns ${{\color{Goldenrod}{ █▏ }}}$ 4.7%, 866 B
dist/utilities/getGlobalData.js ${{\color{Goldenrod}{ █ }}}$ 4.1%, 762 B
dist/utilities/api.js ${{\color{Goldenrod}{ █ }}}$ 4.1%, 756 B
dist/utilities/groupNavItems.js ${{\color{Goldenrod}{ █ }}}$ 4.0%, 745 B
dist/utilities/traverseForLocalizedFields.js ${{\color{Goldenrod}{ ▊ }}}$ 3.1%, 570 B
dist/elements/Translation ${{\color{Goldenrod}{ ▋ }}}$ 2.7%, 493 B
dist/utilities/handleTakeOver.js ${{\color{Goldenrod}{ ▌ }}}$ 2.4%, 440 B
dist/elements/withMergedProps ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 339 B
dist/utilities/getNavGroups.js ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 338 B
dist/utilities/getVisibleEntities.js ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 329 B
dist/elements/WithServerSideProps ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 232 B
dist/layouts/Root ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 230 B
dist/utilities/handleGoBack.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 180 B
dist/fields/mergeFieldStyles.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 158 B
dist/forms/Form ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 152 B
dist/utilities/handleBackToDashboard.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 152 B
(other) ${{\color{Goldenrod}{ █████████████████▍ }}}$ 69.6%, 12.84 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.

@PP-Tom

PP-Tom commented Oct 8, 2026

Copy link
Copy Markdown

This is great - do you think the focus point should also change to keep it's same percentage. It threw me at first because it crops center but the focus point is on the right, should this keep to the center as you drag?

@nathanlentz

Copy link
Copy Markdown
Collaborator Author

This is great - do you think the focus point should also change to keep it's same percentage. It threw me at first because it crops center but the focus point is on the right, should this keep to the center as you drag?

Do you mean the focal point should stay at the same relative position within the crop (so a centered point stays centered as you drag) or stay anchored to the same subject in the original image?

@PP-Tom

PP-Tom commented Oct 9, 2026 •

Copy link
Copy Markdown

This is great - do you think the focus point should also change to keep it's same percentage. It threw me at first because it crops center but the focus point is on the right, should this keep to the center as you drag?

Do you mean the focal point should stay at the same relative position within the crop (so a centered point stays centered as you drag) or stay anchored to the same subject in the original image?

Staying anchored to the same subject sounds like a really good user exerpeince. Though I was just meaning keeping the same relative position, in the video I saw a disconnect where the focus point is and where the crop actually ends up.

This branch has not been deployed

No deployments
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.

2 participants