Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Admin UI RSC error with nested array fields in Next.js 15 + React 19 #14593

@Pymsh1993

Description

@Pymsh1993

Describe the Bug

Payload CMS Admin UI RSC Issue Report

Issue Summary

When using Next.js 15.4.7 + React 19 + Payload CMS 3.61.1, the Payload admin UI throws an RSC (React Server Components) serialization error when attempting to add new items to array fields within globals, despite the underlying data structure being fully RSC-compatible.


Environment

  • Next.js: 15.4.7
  • React: 19.0.0
  • Payload CMS: 3.61.1
  • MongoDB: Database backend
  • Editor: slateEditor({}) (global config)

Error Message

Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.
  {}
    at resolveErrorDev (webpack-internal:///(app-pages-browser)/../../node_modules/.pnpm/[email protected]_.../react-server-dom-webpack-client.browser.development.js:2172:46)
    at processFullStringRow (...)
    at processFullBinaryRow (...)
    at processBinaryChunk (...)

Reproduction Steps

Global Config Structure

export const SiteBannersV3: GlobalConfig = {
  slug: 'site-banners-v3',
  fields: [
    { name: 'enabled', type: 'checkbox' },
    {
      name: 'top',
      type: 'group',
      fields: [
        { name: 'enabled', type: 'checkbox' },
        { name: 'rotationInterval', type: 'number' },
        {
          name: 'banners',
          type: 'array',
          fields: [
            { name: 'active', type: 'checkbox' },
            { name: 'startDate', type: 'date' },
            { name: 'endDate', type: 'date' },
            {
              name: 'messages',
              type: 'array',
              minRows: 1,
              maxRows: 3,
              fields: [
                { name: 'text', type: 'textarea' },
                { name: 'linkText', type: 'text' },
                { name: 'linkUrl', type: 'text' },
                { name: 'backgroundColor', type: 'text' },
                { name: 'textColor', type: 'text' },
              ],
            },
          ],
        },
      ],
    },
  ],
}

Steps to Reproduce

  1. Create a global with nested array fields (as shown above)
  2. Visit /admin/globals/site-banners-v3 in Payload admin
  3. Click "Add Banner" button to add an item to the banners array
  4. Error occurs during form rendering

Key Observations

  • Admin UI "Add" button: Throws RSC error
  • Viewing existing data: Works perfectly
  • Editing existing data: Works perfectly (despite showing the error, saves successfully)
  • Frontend rendering: Works perfectly (all data displays correctly)
  • Direct MongoDB insertion: Works perfectly

Workaround Solution

What Works

We successfully bypassed the admin UI by inserting data directly via MongoDB:

// Force-create banners via MongoDB client
const bannerData = {
  globalType: 'site-banners-v3',
  enabled: true,
  top: {
    banners: [/* array of banner objects */]
  }
}

await db.collection('globals').updateOne(
  { globalType: 'site-banners-v3' },
  { $set: bannerData },
  { upsert: true }
)

Result

  • ✅ Data created successfully
  • ✅ Payload admin displays the data correctly
  • ✅ Frontend renders the data correctly
  • ✅ Users can edit and save existing items (despite the error appearing)
  • ❌ Users cannot add new items via admin UI (error blocks creation)

Root Cause Analysis

Our Investigation

  1. Data structure is NOT the problem - We validated that all data is RSC-safe (primitives only: strings, numbers, booleans)
  2. Frontend rendering is NOT the problem - All Server Components work correctly with the data
  3. The issue is in Payload admin's form rendering - Something in the admin UI's array field rendering crosses an RSC boundary improperly

Suspected Causes

  • Date picker components (type: 'date') might be creating Date objects during form rendering
  • Nested array field UI might be using complex components that aren't serializable
  • Conditional field rendering might be passing functions across RSC boundary
  • Form metadata/state might include classes or null-prototyped objects

Impact

  • Severity: Medium - Users can still manage content via editing, but cannot add new items
  • User Experience: Confusing - Error appears but saves still work for existing items
  • Workaround Complexity: Low - Direct database insertion works, but requires technical knowledge

Questions for Payload Team

  1. Is this a known issue with Payload 3.x + Next.js 15 + React 19?
  2. Are nested array fields tested with RSC in Payload admin UI?
  3. Are date pickers RSC-compatible in the admin UI?
  4. Is there a recommended pattern for complex nested structures in globals?
  5. Should we file a GitHub issue with reproduction steps?

Expected Behavior

Clicking "Add Banner" should render an empty form for creating a new banner item without throwing RSC serialization errors.


Additional Context

  • We removed admin.condition functions (arrow functions are not serializable)
  • We removed pickerAppearance: 'dayAndTime' from date fields (complex picker UI)
  • We removed afterChange hooks that used revalidateTag()
  • All of these removals did NOT fix the issue

Contact Information


Summary

The Payload admin UI has an RSC boundary issue when rendering forms for adding new array items in globals. The data structure itself is correct (proven by direct MongoDB insertion + frontend rendering), but the admin UI's form rendering crosses an RSC boundary improperly. Users can edit existing items (despite errors appearing), but cannot add new items via the UI.

Link to the code that reproduces this issue

"Private repo - providing code snippets and reproduction steps instead of link"

Reproduction Steps

Reproduction Steps

Start Next.js dev server: npm run dev
Visit Payload admin: /admin/globals/site-banners-v3
Click "Add Banner" button to add item to banners array
Error occurs immediately during form rendering:

Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.

1. Create Global Config

Create a Payload global with this structure:

// globals/SiteBannersV3.ts
import type { GlobalConfig } from 'payload'

export const SiteBannersV3: GlobalConfig = {
  slug: 'site-banners-v3',
  fields: [
    { name: 'enabled', type: 'checkbox' },
    {
      name: 'top',
      type: 'group',
      fields: [
        { name: 'enabled', type: 'checkbox' },
        { name: 'rotationInterval', type: 'number' },
        {
          name: 'banners',
          type: 'array',
          fields: [
            { name: 'active', type: 'checkbox' },
            { name: 'startDate', type: 'date' },
            { name: 'endDate', type: 'date' },
            {
              name: 'messages',
              type: 'array',
              minRows: 1,
              maxRows: 3,
              fields: [
                { name: 'text', type: 'textarea' },
                { name: 'linkText', type: 'text' },
                { name: 'linkUrl', type: 'text' },
                { name: 'backgroundColor', type: 'text' },
                { name: 'textColor', type: 'text' },
              ],
            },
          ],
        },
      ],
    },
  ],
}





. Verify Workaround
To prove data structure is fine, insert via MongoDB:


const { MongoClient } = require('mongodb')
const client = new MongoClient(process.env.DATABASE_URI)
await client.connect()

await client.db().collection('globals').updateOne(
  { globalType: 'site-banners-v3' },
  {
    $set: {
      globalType: 'site-banners-v3',
      enabled: true,
      top: {
        enabled: true,
        banners: [{
          active: true,
          startDate: new Date().toISOString(),
          endDate: new Date(Date.now() + 30*24*60*60*1000).toISOString(),
          messages: [{
            text: 'Test Banner',
            linkText: 'Click',
            linkUrl: '/test',
            backgroundColor: '#000000',
            textColor: '#ffffff'
          }]
        }]
      }
    }
  },
  { upsert: true }
)

### Which area(s) are affected? (Select all that apply)

area: ui, area: templates, area: core

### Environment Info

```text
// payload.config.ts
import { SiteBannersV3 } from './globals/SiteBannersV3'

export default buildConfig({
  globals: [SiteBannersV3],
  // ... rest of config
})


affected areas are:
area: admin (primary issue location)
area: fields (specifically array fields)

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: needs-triagePossible bug which hasn't been reproduced yet

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions