From 96074530b1106b521c1c6b28c8ed3659b45f1e34 Mon Sep 17 00:00:00 2001
From: Patrik <35232443+PatrikKozak@users.noreply.github.com>
Date: Thu, 21 Aug 2025 13:23:51 -0400
Subject: [PATCH] fix: server edit view components don't receive document id
prop (#13526)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
### What?
Make the document `id` available to server-rendered admin components
that expect it—specifically `EditMenuItems` and
`BeforeDocumentControls`—so `props.id` matches the official docs.
### Why?
The docs show examples using `props.id`, but the runtime `serverProps`
and TS types didn’t include it. This led to `undefined` at render time.
### How?
- Add id to ServerProps and set it in renderDocumentSlots from
req.routeParams.id.
Fixes #13420
---
docs/custom-components/edit-view.mdx | 11 ++---
.../views/Document/renderDocumentSlots.tsx | 2 +-
packages/payload/src/config/types.ts | 1 +
test/admin/collections/editMenuItems.ts | 5 +-
.../components/EditMenuItemsServer/index.tsx | 13 +++++
test/admin/e2e/document-view/e2e.spec.ts | 47 ++++++++++++++++++-
6 files changed, 70 insertions(+), 9 deletions(-)
create mode 100644 test/admin/components/EditMenuItemsServer/index.tsx
diff --git a/docs/custom-components/edit-view.mdx b/docs/custom-components/edit-view.mdx
index 92a4e3a77..af7f371ce 100644
--- a/docs/custom-components/edit-view.mdx
+++ b/docs/custom-components/edit-view.mdx
@@ -293,7 +293,6 @@ Here's an example of a custom `editMenuItems` component:
```tsx
import React from 'react'
-import { PopupList } from '@payloadcms/ui'
import type { EditMenuItemsServerProps } from 'payload'
@@ -301,12 +300,12 @@ export const EditMenuItems = async (props: EditMenuItemsServerProps) => {
const href = `/custom-action?id=${props.id}`
return (
-
- Custom Edit Menu Item
-
+ <>
+ Custom Edit Menu Item
+
Another Custom Edit Menu Item - add as many as you need!
-
-
+
+ >
)
}
```
diff --git a/packages/next/src/views/Document/renderDocumentSlots.tsx b/packages/next/src/views/Document/renderDocumentSlots.tsx
index 8d5f7dbcc..7b81e3dae 100644
--- a/packages/next/src/views/Document/renderDocumentSlots.tsx
+++ b/packages/next/src/views/Document/renderDocumentSlots.tsx
@@ -1,6 +1,5 @@
import type {
BeforeDocumentControlsServerPropsOnly,
- DefaultServerFunctionArgs,
DocumentSlots,
EditMenuItemsServerPropsOnly,
PayloadRequest,
@@ -39,6 +38,7 @@ export const renderDocumentSlots: (args: {
const isPreviewEnabled = collectionConfig?.admin?.preview || globalConfig?.admin?.preview
const serverProps: ServerProps = {
+ id: req.routeParams.id as number | string,
i18n: req.i18n,
payload: req.payload,
user: req.user,
diff --git a/packages/payload/src/config/types.ts b/packages/payload/src/config/types.ts
index fdd2e460f..82c6f132c 100644
--- a/packages/payload/src/config/types.ts
+++ b/packages/payload/src/config/types.ts
@@ -402,6 +402,7 @@ export type Params = { [key: string]: string | string[] | undefined }
export type ServerProps = {
readonly documentSubViewType?: DocumentSubViewTypes
readonly i18n: I18nClient
+ readonly id?: number | string
readonly locale?: Locale
readonly params?: Params
readonly payload: Payload
diff --git a/test/admin/collections/editMenuItems.ts b/test/admin/collections/editMenuItems.ts
index f08f30352..278318c0d 100644
--- a/test/admin/collections/editMenuItems.ts
+++ b/test/admin/collections/editMenuItems.ts
@@ -1,6 +1,6 @@
import type { CollectionConfig } from 'payload'
-import { editMenuItemsSlug, postsCollectionSlug, uploadCollectionSlug } from '../slugs.js'
+import { editMenuItemsSlug } from '../slugs.js'
export const EditMenuItems: CollectionConfig = {
slug: editMenuItemsSlug,
@@ -11,6 +11,9 @@ export const EditMenuItems: CollectionConfig = {
{
path: '/components/EditMenuItems/index.js#EditMenuItems',
},
+ {
+ path: '/components/EditMenuItemsServer/index.js#EditMenuItemsServer',
+ },
],
},
},
diff --git a/test/admin/components/EditMenuItemsServer/index.tsx b/test/admin/components/EditMenuItemsServer/index.tsx
new file mode 100644
index 000000000..3bd5aaa33
--- /dev/null
+++ b/test/admin/components/EditMenuItemsServer/index.tsx
@@ -0,0 +1,13 @@
+import type { EditMenuItemsServerProps } from 'payload'
+
+import React from 'react'
+
+export const EditMenuItemsServer = (props: EditMenuItemsServerProps) => {
+ const href = `/custom-action?id=${props.id}`
+
+ return (
+
+ )
+}
diff --git a/test/admin/e2e/document-view/e2e.spec.ts b/test/admin/e2e/document-view/e2e.spec.ts
index a4705ae0e..f793332a8 100644
--- a/test/admin/e2e/document-view/e2e.spec.ts
+++ b/test/admin/e2e/document-view/e2e.spec.ts
@@ -731,7 +731,7 @@ describe('Document View', () => {
})
describe('custom editMenuItem components', () => {
- test('should render custom editMenuItems component', async () => {
+ test('should render custom editMenuItems client component', async () => {
await page.goto(editMenuItemsURL.create)
await page.locator('#field-title')?.fill(title)
await saveDocAndAssert(page)
@@ -746,6 +746,51 @@ describe('Document View', () => {
await expect(customEditMenuItem).toBeVisible()
})
+
+ test('should render custom editMenuItems server component', async () => {
+ await page.goto(editMenuItemsURL.create)
+ await page.locator('#field-title')?.fill(title)
+ await saveDocAndAssert(page)
+
+ const threeDotMenu = page.getByRole('main').locator('.doc-controls__popup')
+ await expect(threeDotMenu).toBeVisible()
+ await threeDotMenu.click()
+
+ const popup = page.locator('.popup--active .popup__content')
+ await expect(popup).toBeVisible()
+
+ const customEditMenuItem = popup.getByRole('link', {
+ name: 'Custom Edit Menu Item (Server)',
+ })
+
+ await expect(customEditMenuItem).toBeVisible()
+ })
+
+ test('should render doc id in href of custom editMenuItems server component link', async () => {
+ await page.goto(editMenuItemsURL.create)
+ await page.locator('#field-title')?.fill(title)
+ await saveDocAndAssert(page)
+
+ const threeDotMenu = page.getByRole('main').locator('.doc-controls__popup')
+ await expect(threeDotMenu).toBeVisible()
+ await threeDotMenu.click()
+
+ const popup = page.locator('.popup--active .popup__content')
+ await expect(popup).toBeVisible()
+
+ const customEditMenuItem = popup.getByRole('link', {
+ name: 'Custom Edit Menu Item (Server)',
+ })
+
+ await expect(customEditMenuItem).toBeVisible()
+
+ // Extract the document id from the edit page URL (last path segment)
+ const editPath = new URL(page.url()).pathname
+ const docId = editPath.split('/').filter(Boolean).pop()!
+
+ // Assert the href contains the same id
+ await expect(customEditMenuItem).toHaveAttribute('href', `/custom-action?id=${docId}`)
+ })
})
})