diff --git a/web/app/(commonLayout)/marketplace/__tests__/layout.spec.tsx b/web/app/(commonLayout)/marketplace/__tests__/layout.spec.tsx
new file mode 100644
index 00000000000..1e18f9f6b3b
--- /dev/null
+++ b/web/app/(commonLayout)/marketplace/__tests__/layout.spec.tsx
@@ -0,0 +1,33 @@
+import { readFileSync } from 'node:fs'
+import { dirname, resolve } from 'node:path'
+import { fileURLToPath } from 'node:url'
+import { render, screen } from '@testing-library/react'
+import { describe, expect, it, vi } from 'vitest'
+
+vi.mock('../document-title', () => ({
+ default: () => marketplace document title,
+}))
+
+describe('marketplace route layout', () => {
+ it('stays a server module so Flight can stream the async marketplace page', () => {
+ const source = readFileSync(
+ resolve(dirname(fileURLToPath(import.meta.url)), '../layout.tsx'),
+ 'utf8',
+ )
+
+ expect(source).not.toMatch(/^['"]use client['"]/)
+ })
+
+ it('renders marketplace children and the document title island', async () => {
+ const { default: MarketplaceLayout } = await import('../layout')
+
+ render(
+
+ marketplace page
+ ,
+ )
+
+ expect(screen.getByText('marketplace document title')).toBeInTheDocument()
+ expect(screen.getByText('marketplace page')).toBeInTheDocument()
+ })
+})
diff --git a/web/app/(commonLayout)/marketplace/document-title.tsx b/web/app/(commonLayout)/marketplace/document-title.tsx
new file mode 100644
index 00000000000..d053ab9cb3c
--- /dev/null
+++ b/web/app/(commonLayout)/marketplace/document-title.tsx
@@ -0,0 +1,12 @@
+'use client'
+
+import { useTranslation } from 'react-i18next'
+import useDocumentTitle from '@/hooks/use-document-title'
+
+const MarketplaceDocumentTitle = () => {
+ const { t } = useTranslation()
+ useDocumentTitle(t(($) => $['mainNav.marketplace'], { ns: 'common' }))
+ return null
+}
+
+export default MarketplaceDocumentTitle
diff --git a/web/app/(commonLayout)/marketplace/layout.tsx b/web/app/(commonLayout)/marketplace/layout.tsx
index 5a40a6a3a62..7dbbf002cdd 100644
--- a/web/app/(commonLayout)/marketplace/layout.tsx
+++ b/web/app/(commonLayout)/marketplace/layout.tsx
@@ -1,12 +1,14 @@
-'use client'
-
import type { PropsWithChildren } from 'react'
-import { useTranslation } from 'react-i18next'
-import useDocumentTitle from '@/hooks/use-document-title'
+import MarketplaceDocumentTitle from './document-title'
+// This route layout must stay a Server Component. A client layout wrapping the
+// async marketplace page makes Flight double-resolve streamed children
+// (`reason.enqueueModel`) when opening /marketplace from cloud.dify.dev.
export default function MarketplaceLayout({ children }: PropsWithChildren) {
- const { t } = useTranslation()
- useDocumentTitle(t(($) => $['mainNav.marketplace'], { ns: 'common' }))
-
- return children
+ return (
+ <>
+
+ {children}
+ >
+ )
}