Skip to content

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

@ioExpander

Description

@ioExpander

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

> [email protected] 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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions