Skip to content

Virtual fields run client-side validation despite being non-editable #16012

@jhb-dev

Description

@jhb-dev

Describe the Bug

Virtual fields (virtual: true or virtual: "path") show validation errors in the admin UI even though the user cannot edit them. The value is populated server-side (via afterRead hooks, built-in virtual population, or plugin logic), so it's undefined in the form state — causing required/minLength/etc. validators to fail.

This affects all virtual field variants:

  • virtual: "post.title" — auto-populated by Payload
  • virtual: true with afterRead hook — e.g. computed reading time
  • virtual: true without hooks — e.g. fields populated by plugins or used for type generation only

Reproduction Steps

  1. Add a virtual field with required: true to a collection:
{
  name: 'estimatedReadingTime',
  type: 'text',
  virtual: true,
  required: true,
  hooks: {
    afterRead: [({ data }) => `${Math.ceil((data?.content?.split(/\s+/).length ?? 0) / 200)} min read`],
  },
}
  1. Open the admin panel, create or edit a document in that collection
  2. The field shows "This field is required" — the user cannot resolve this

Expected behavior

Virtual fields should skip client-side validation and be read-only by default, since their value is never user-provided.

Root cause

In packages/payload/src/fields/config/sanitize.ts:273, every data field without an explicit validate gets a default type-based validator — no exception for virtual fields. In packages/ui/src/forms/fieldSchemasToFormState/addFieldStatePromise.ts:216, validation runs on all fields with no virtual check. And RenderFields never auto-sets readOnly for virtual fields.

Workaround

{
  name: 'estimatedReadingTime',
  type: 'text',
  virtual: true,
  validate: () => true,
  admin: { readOnly: true },
  // ...
}

Affected areas

  • area: core
  • area: ui

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions