payloadcms / payloadcms/payload

[Perf] Admin UI edit forms call a server action on every keypress. Resulting in potentially HUGE vercel bills.

Open
#13,753 10 comments 16 reactions 1 assignee View on GitHub

@jacobsfletch is already working on this.

Since Sep 9, 2025.

created-by: Contributor
Dominant language
TypeScript
Stars
44.8k
Forks
4.2k
Avg merge
2d 21h
Merged PRs (30d)
53

Description

Describe the Bug

On my newly deployed payload project on Vercel I've recieved a usage alert which was strange as I had a single user connected to the app.

Upon investigation, it seems that the incoming fast origin transferis way too high :
Image

Digging deeper, the high upload is comming from Payload's admin UI :
Image

Gigabytes of upload in a couple of hours, with text documents only (no files in the CMS) by a single user editing the documents, is probably not normal.

Digging deeper, it seems that, when editing documents in the Admin UI, on every keystroke, the admin sends a POST request named form-state containing the full document object. This can grow exponentially huge as editors edit bigger and bigger documents using the admin.

I might have found at least one issue in the file payload/packages/ui/src/views/Edit/index.tsx
The onChange handler of the Form calls the server action getFormState and sends the prevFormState of the form which contains the full form data without any throttling 🙁 This also affects other forms in the admin UI as far as I can tell. But this one in the main edit Form is really causing a ton of server calls.

const onChange: FormProps['onChange'][0] = useCallback(
    async ({ formState: prevFormState, submitted }) => {
      const controller = handleAbortRef(abortOnChangeRef)

      const currentTime = Date.now()
      const timeSinceLastUpdate = currentTime - editSessionStartTime

      const updateLastEdited = isLockingEnabled && timeSinceLastUpdate >= 10000 // 10 seconds

      if (updateLastEdited) {
        setEditSessionStartTime(currentTime)
      }

      const docPreferences = await getDocPreferences()

      const result = await getFormState({// <--- this server action sends the POST request containing the form data
        id,
        collectionSlug,
        docPermissions,
        docPreferences,
        formState: prevFormState, // <--- this contains the full form data and can be HUGE
        globalSlug,
        operation,
        renderAllFields: false,
        returnLockStatus: isLockingEnabled,
        schemaPath: schemaPathSegments.join('.'),
        signal: controller.signal,
        skipValidation: !submitted,
        updateLastEdited,
      })
Link to the code that reproduces this issue

https://github.com/ioExpander/payload-perf-issue

Reproduction Steps
  1. pnpx create-payload-app -t blank
  2. Setup DB connection in .env (I'm using mongoDB)
  3. pnpm dev
  4. Go to admin, create first user
  5. In the admin, select the Users Collection and edit the newly created user
  6. Open Devtools->Network
  7. Type anything in the "email" field of the user.
  8. You should see POST requests firing on every keypress containing the whole User document
Which area(s) are affected? (Select all that apply)

area: core, area: ui

Environment Info
> issue@1.0.0 payload /workspaces/payload-perf-issue
> cross-env NODE_OPTIONS=--no-deprecation payload info


Binaries:
  Node: 22.16.0
  npm: 10.9.2
  Yarn: 1.22.22
  pnpm: 10.11.0
Relevant Packages:
  payload: 3.54.0
  next: 15.4.4
  @payloadcms/db-mongodb: 3.54.0
  @payloadcms/next/utilities: 3.54.0
  @payloadcms/payload-cloud: 3.54.0
  @payloadcms/richtext-lexical: 3.54.0
  @payloadcms/ui/shared: 3.54.0
  react: 19.1.0
Operating System:
  Platform: linux
  Arch: x64
  Version: #1 SMP PREEMPT_DYNAMIC Debian 6.1.140-1 (2025-05-22)
  Available memory (MB): 19539
  Available CPU cores: 7

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.