Describe the Bug
A virtual field using dot-notation (e.g., virtual: "relation.field") fails to populate with data if the target field in the related collection is itself a virtual/hook-populated field generated during afterRead.
In this scenario, the parent collection's virtual field returns undefined or an empty string, likely because the virtual field resolution occurs before the afterRead hooks of the populated relationship have finished executing.
Expected Behaviour
The virtual field authorName should correctly display the value generated by the afterRead hook of the related author document.
Actual Behaviour
The field remains empty. If the fullName field in the Users collection is changed to a standard stored text field (no hooks), the virtual field works as expected.
Link to the code that reproduces this issue
https://github.com/max-atkins/payload-virtual-field-blank
Reproduction Steps
- Create a Users collection with a fullName field that uses an afterRead hook to combine firstName and lastName.
- Create a Posts collection with a relationship field to Users.
- Add a virtual field to Posts named authorName with virtual: "author.fullName".
- Create a User, then create a Post linked to that User.
- Observe that authorName in the Admin UI or API response is empty/null, despite author.fullName being visible when inspecting the expanded relationship.
{
slug: "users",
fields: [
{ name: "firstName", type: "text", required: true },
{ name: "lastName", type: "text", required: true },
{
name: "fullName",
type: "text",
admin: { readOnly: true },
hooks: {
afterRead: [({ data }) => `${data.firstName} ${data.lastName}`], // Simplified for example
},
},
],
}
{
slug: "posts",
fields: [
{
name: "author",
type: "relationship",
relationTo: "users",
},
{
name: "authorName",
type: "text",
virtual: "author.fullName",
admin: { readOnly: true },
}
]
}
Which area(s) are affected?
area: core
Environment Info
Binaries:
Node: 24.11.0
npm: 11.6.1
Yarn: 1.22.22
pnpm: 10.28.0
Relevant Packages:
payload: 3.79.0
next: 16.1.6
@payloadcms/db-postgres: 3.79.0
@payloadcms/drizzle: 3.79.0
@payloadcms/graphql: 3.79.0
@payloadcms/live-preview: 3.79.0
@payloadcms/live-preview-react: 3.79.0
@payloadcms/next/utilities: 3.79.0
@payloadcms/plugin-cloud-storage: 3.79.0
@payloadcms/plugin-multi-tenant: 3.79.0
@payloadcms/richtext-lexical: 3.79.0
@payloadcms/storage-s3: 3.79.0
@payloadcms/translations: 3.79.0
@payloadcms/ui/shared: 3.79.0
react: 19.2.3
react-dom: 19.2.3
Operating System:
Platform: linux
Arch: x64
Version: #14~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Jan 15 15:52:10 UTC 2
Available memory (MB): 31776
Available CPU cores: 32
Describe the Bug
A virtual field using dot-notation (e.g., virtual: "relation.field") fails to populate with data if the target field in the related collection is itself a virtual/hook-populated field generated during afterRead.
In this scenario, the parent collection's virtual field returns undefined or an empty string, likely because the virtual field resolution occurs before the afterRead hooks of the populated relationship have finished executing.
Expected Behaviour
The virtual field authorName should correctly display the value generated by the afterRead hook of the related author document.
Actual Behaviour
The field remains empty. If the fullName field in the Users collection is changed to a standard stored text field (no hooks), the virtual field works as expected.
Link to the code that reproduces this issue
https://github.com/max-atkins/payload-virtual-field-blank
Reproduction Steps
Which area(s) are affected?
area: core
Environment Info