Skip to content

feat!: admin server adapter#16753

Merged
jacobsfletch merged 9 commits into
mainfrom
refactor/server-adapter-abstraction
May 28, 2026
Merged

feat!: admin server adapter#16753
jacobsfletch merged 9 commits into
mainfrom
refactor/server-adapter-abstraction

Conversation

@jacobsfletch

@jacobsfletch jacobsfletch commented May 27, 2026

Copy link
Copy Markdown
Member

Decouples plugins and server components from Next.js APIs, e.g. headers, cookies, server-side navigation, etc. This way we can fully support alternative React frameworks other than Next.js, e.g. TanStack.

This includes:

  • getCookies
  • getHeaders
  • redirect
  • notFound

These methods are now accessible behind a new server object. Each framework is responsible for adapting its own methods into this standard format.

Before

Before, you'd use direct imports from next/*:

import { cookies, headers } from 'next/headers.js'
import { notFound, redirect } from 'next/navigation.js'

export const MyPluginView = async ({ payload }: ServerProps) => {
  const reqHeaders = await headers()
  const reqCookies = await cookies()
  const session = reqCookies.get('session')?.value

  if (!session) {
    redirect('/login')
  }
}

After

After, there are now framework-agnostic methods accessible via req.server:

export const MyPluginView = async ({ req }: ServerProps) => {
  const reqHeaders = await req.server.getHeaders()
  const reqCookies = await req.server.getCookies()
  const session = reqCookies.get('session')?.value

  if (!session) {
    req.server.redirect('/login')
  }
}

In custom server components, this is provided to you as a new server prop:

const MyServerComponent: React.FC<TextFieldServerProps> = ({ server  }) => {
  const cookies = await server.getCookies()
  // ...
}

Writing your own Server Adapter

To write a server adapter, you must provide these methods using your framework's proprietary APIs.

Here's an example of what a Next.js server adapter might look like (simplified):

import type { ServerAdapter } from 'payload'

import { headers as getNextHeaders } from 'next/headers.js'

import {
  notFound as nextNotFound,
  redirect as nextRedirect,
} from 'next/navigation.js'

export const nextServerAdapter: ServerAdapter = {
  getHeaders: () => getNextHeaders(),
  notFound: () => nextNotFound(),
  redirect: (path) => nextRedirect(path),
  // ...
}

@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

📦 esbuild Bundle Analysis for payload

This analysis was generated by esbuild-bundle-analyzer. 🤖

Meta File Out File Size (raw) Note
packages/next/meta_index.json esbuild/index.js 1.04 MB 🆕 Added
packages/payload/meta_index.json esbuild/index.js 1.41 MB 🆕 Added
packages/payload/meta_shared.json esbuild/exports/shared.js 192.51 KB 🆕 Added
packages/richtext-lexical/meta_client.json esbuild/exports/client_optimized/index.js 304.12 KB 🆕 Added
packages/ui/meta_client.json esbuild/exports/client_optimized/index.js 1.25 MB 🆕 Added
packages/ui/meta_shared.json esbuild/exports/shared_optimized/index.js 18.56 KB 🆕 Added
Largest paths These visualization shows top 20 largest paths in the bundle.

Meta file: packages/next/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ████████████████████▋ }}}$ 82.8%, 861.29 KB
dist/views/Version ${{\color{Goldenrod}{ █▎ }}}$ 5.0%, 51.56 KB
dist/views/Dashboard ${{\color{Goldenrod}{ ▌ }}}$ 2.1%, 21.85 KB
dist/views/Document ${{\color{Goldenrod}{ ▍ }}}$ 1.6%, 16.73 KB
dist/views/List ${{\color{Goldenrod}{ ▍ }}}$ 1.5%, 15.50 KB
dist/elements/Nav ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 10.26 KB
dist/views/Root ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 9.93 KB
dist/views/API ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.13 KB
dist/views/Account ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.08 KB
dist/views/Versions ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 5.74 KB
dist/elements/DocumentHeader ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 4.69 KB
dist/views/Login ${{\color{Goldenrod}{ }}}$ 0.4%, 4.57 KB
dist/layouts/Root ${{\color{Goldenrod}{ }}}$ 0.3%, 3.56 KB
dist/views/ForgotPassword ${{\color{Goldenrod}{ }}}$ 0.3%, 3.15 KB
dist/views/CreateFirstUser ${{\color{Goldenrod}{ }}}$ 0.3%, 3.02 KB
dist/views/ResetPassword ${{\color{Goldenrod}{ }}}$ 0.2%, 2.42 KB
dist/templates/Default ${{\color{Goldenrod}{ }}}$ 0.2%, 2.04 KB
dist/views/Logout ${{\color{Goldenrod}{ }}}$ 0.2%, 1.96 KB
dist/views/Verify ${{\color{Goldenrod}{ }}}$ 0.1%, 1.31 KB
dist/utilities/initReq.js ${{\color{Goldenrod}{ }}}$ 0.1%, 1.15 KB
(other) ${{\color{Goldenrod}{ ████▎ }}}$ 17.2%, 178.34 KB

Meta file: packages/payload/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ █████████████████ }}}$ 68.4%, 959.51 KB
dist/fields/hooks ${{\color{Goldenrod}{ ▊ }}}$ 3.1%, 44.07 KB
dist/collections/operations ${{\color{Goldenrod}{ ▋ }}}$ 2.9%, 40.23 KB
dist/versions/migrations ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 18.50 KB
dist/auth/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 15.63 KB
dist/fields/config ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 13.41 KB
dist/globals/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 13.40 KB
dist/utilities/configToJSONSchema.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 13.13 KB
dist/queues/operations ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 12.63 KB
dist/fields/validations.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 10.57 KB
dist/collections/config ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 9.53 KB
dist/bin/generateImportMap ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 9.44 KB
dist/config/orderable ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.92 KB
dist/uploads/fetchAPI-multipart ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.80 KB
dist/index.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.77 KB
dist/hierarchy/utils ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 7.65 KB
dist/database/migrations ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 7.54 KB
dist/collections/endpoints ${{\color{Goldenrod}{ }}}$ 0.4%, 6.23 KB
dist/auth/strategies ${{\color{Goldenrod}{ }}}$ 0.4%, 5.43 KB
dist/queues/config ${{\color{Goldenrod}{ }}}$ 0.4%, 5.31 KB
(other) ${{\color{Goldenrod}{ ███████▉ }}}$ 31.6%, 443.37 KB

Meta file: packages/payload/meta_shared.json, Out file: esbuild/exports/shared.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ███████████████████▉ }}}$ 79.5%, 150.12 KB
dist/fields/validations.js ${{\color{Goldenrod}{ █▍ }}}$ 5.6%, 10.57 KB
dist/config/orderable ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 3.13 KB
dist/fields/baseFields ${{\color{Goldenrod}{ ▍ }}}$ 1.5%, 2.79 KB
dist/utilities/deepCopyObject.js ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 2.54 KB
dist/auth/cookies.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 1.55 KB
dist/utilities/flattenTopLevelFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 1.42 KB
dist/fields/config ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 1.29 KB
dist/utilities/getVersionsConfig.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 1.04 KB
dist/utilities/flattenAllFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 943 B
dist/utilities/unflatten.js ${{\color{Goldenrod}{ }}}$ 0.4%, 779 B
dist/utilities/sanitizeUserDataForEmail.js ${{\color{Goldenrod}{ }}}$ 0.4%, 713 B
dist/utilities/getFieldPermissions.js ${{\color{Goldenrod}{ }}}$ 0.3%, 651 B
dist/collections/config ${{\color{Goldenrod}{ }}}$ 0.3%, 570 B
dist/bin/generateImportMap ${{\color{Goldenrod}{ }}}$ 0.3%, 561 B
dist/auth/sessions.js ${{\color{Goldenrod}{ }}}$ 0.3%, 525 B
dist/fields/getFieldPaths.js ${{\color{Goldenrod}{ }}}$ 0.3%, 485 B
dist/utilities/appendDateTimezoneSelectFields.js ${{\color{Goldenrod}{ }}}$ 0.2%, 451 B
dist/utilities/getSafeRedirect.js ${{\color{Goldenrod}{ }}}$ 0.2%, 423 B
dist/utilities/deepMerge.js ${{\color{Goldenrod}{ }}}$ 0.2%, 413 B
(other) ${{\color{Goldenrod}{ █████▏ }}}$ 20.5%, 38.61 KB

Meta file: packages/richtext-lexical/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
dist/features/blocks ${{\color{Goldenrod}{ ███ }}}$ 12.4%, 37.36 KB
dist/lexical/ui ${{\color{Goldenrod}{ ██▊ }}}$ 11.4%, 34.16 KB
dist/lexical/plugins ${{\color{Goldenrod}{ ██▋ }}}$ 10.9%, 32.88 KB
dist/features/experimental_table ${{\color{Goldenrod}{ ██▎ }}}$ 9.0%, 27.16 KB
dist/packages/@lexical ${{\color{Goldenrod}{ █▌ }}}$ 6.3%, 18.99 KB
dist/features/link ${{\color{Goldenrod}{ █▌ }}}$ 6.3%, 18.81 KB
dist/features/toolbars ${{\color{Goldenrod}{ █▍ }}}$ 5.5%, 16.58 KB
dist/features/upload ${{\color{Goldenrod}{ █▏ }}}$ 4.7%, 14.09 KB
dist/features/textState ${{\color{Goldenrod}{ ▉ }}}$ 3.7%, 11.08 KB
dist/features/relationship ${{\color{Goldenrod}{ ▊ }}}$ 3.1%, 9.39 KB
dist/lexical/utils ${{\color{Goldenrod}{ ▋ }}}$ 2.9%, 8.79 KB
dist/features/converters ${{\color{Goldenrod}{ ▋ }}}$ 2.8%, 8.36 KB
dist/features/debug ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 7.40 KB
dist/utilities/fieldsDrawer ${{\color{Goldenrod}{ ▌ }}}$ 2.4%, 7.29 KB
dist/lexical/config ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 5.08 KB
dist/features/lists ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 5.00 KB
dist/features/format ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 3.46 KB
dist/lexical/LexicalEditor.js ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 3.23 KB
dist/features/horizontalRule ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 3.18 KB
dist/field/Field.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 2.84 KB
(other) ${{\color{Goldenrod}{ █████████████████████▉ }}}$ 87.6%, 263.53 KB

Meta file: packages/ui/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ███████████▋ }}}$ 46.7%, 579.57 KB
dist/elements/Hierarchy ${{\color{Goldenrod}{ ▉ }}}$ 3.6%, 44.06 KB
dist/elements/BulkUpload ${{\color{Goldenrod}{ ▌ }}}$ 2.3%, 28.06 KB
dist/views/HierarchyList ${{\color{Goldenrod}{ ▍ }}}$ 1.6%, 19.68 KB
dist/elements/Table ${{\color{Goldenrod}{ ▍ }}}$ 1.6%, 19.25 KB
dist/elements/WhereBuilder ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 17.28 KB
dist/views/Edit ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 17.28 KB
dist/forms/Form ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 15.87 KB
dist/fields/Relationship ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 15.48 KB
dist/fields/Blocks ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 15.12 KB
dist/fields/Upload ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 14.39 KB
dist/elements/QueryPresets ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 10.08 KB
dist/elements/PublishButton ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 9.01 KB
dist/elements/HTMLDiff ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.38 KB
dist/elements/LivePreview ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.34 KB
dist/views/List ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.97 KB
dist/elements/ReactSelect ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.84 KB
dist/fields/Array ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.76 KB
dist/elements/RelationshipTable ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.88 KB
dist/elements/Upload ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 6.61 KB
(other) ${{\color{Goldenrod}{ █████████████▎ }}}$ 53.3%, 660.25 KB

Meta file: packages/ui/meta_shared.json, Out file: esbuild/exports/shared_optimized/index.js

Path Size
dist/graphics/Logo ${{\color{Goldenrod}{ ███████▊ }}}$ 31.1%, 5.57 KB
../../node_modules ${{\color{Goldenrod}{ ███▋ }}}$ 14.8%, 2.65 KB
dist/graphics/Icon ${{\color{Goldenrod}{ ██▏ }}}$ 8.5%, 1.52 KB
dist/utilities/formatDocTitle ${{\color{Goldenrod}{ █▊ }}}$ 7.4%, 1.32 KB
dist/providers/TableColumns ${{\color{Goldenrod}{ █▏ }}}$ 4.8%, 866 B
dist/utilities/getGlobalData.js ${{\color{Goldenrod}{ █ }}}$ 4.2%, 762 B
dist/utilities/api.js ${{\color{Goldenrod}{ █ }}}$ 4.2%, 756 B
dist/utilities/groupNavItems.js ${{\color{Goldenrod}{ █ }}}$ 4.1%, 734 B
dist/elements/Translation ${{\color{Goldenrod}{ ▋ }}}$ 2.7%, 493 B
dist/utilities/handleTakeOver.js ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 440 B
dist/utilities/traverseForLocalizedFields.js ${{\color{Goldenrod}{ ▌ }}}$ 2.2%, 399 B
dist/elements/withMergedProps ${{\color{Goldenrod}{ ▍ }}}$ 1.9%, 339 B
dist/utilities/getNavGroups.js ${{\color{Goldenrod}{ ▍ }}}$ 1.9%, 338 B
dist/utilities/getVisibleEntities.js ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 329 B
dist/elements/WithServerSideProps ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 232 B
dist/utilities/handleGoBack.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 180 B
dist/fields/mergeFieldStyles.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 159 B
dist/utilities/handleBackToDashboard.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 152 B
dist/forms/Form ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 147 B
dist/utilities/abortAndIgnore.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 146 B
(other) ${{\color{Goldenrod}{ █████████████████▏ }}}$ 68.9%, 12.36 KB
Details

Next to the size is how much the size has increased or decreased compared with the base branch of this PR.

  • ‼️: Size increased by 20% or more. Special attention should be given to this.
  • ⚠️: Size increased in acceptable range (lower than 20%).
  • ✅: No change or even downsized.
  • 🗑️: The out file is deleted: not found in base branch.
  • 🆕: The out file is newly found: will be added to base branch.

@jacobsfletch jacobsfletch force-pushed the refactor/admin-adapter-types branch from 149a4c4 to 709d8f4 Compare May 27, 2026 19:16
@jacobsfletch jacobsfletch changed the base branch from refactor/admin-adapter-types to feat/admin-router-adapter May 27, 2026 19:20
Adds server-side adapter passed into server functions (not React context),
abstracting framework-specific APIs like cookies, headers, redirect, and
forbidden/unauthorized/notFound boundaries.

- Define ServerAdapter type and Next.js implementation
- Wire ServerAdapter through plugin-multi-tenant entrypoints
- Safely access req.server from optional contexts
@jacobsfletch jacobsfletch force-pushed the refactor/server-adapter-abstraction branch from b9942c4 to c7cdd4f Compare May 27, 2026 19:31
Groups framework adapter implementations under packages/next/src/adapters/
instead of mixing them into elements/ (UI primitives) and utilities/ (misc
helpers). The Next adapter is neither — it bridges next/navigation into the
RouterAdapterContext contract from payload core. Sets up the structure for
the upcoming ServerAdapter to live alongside as adapters/server.ts.
Co-locates the Next ServerAdapter impl with the RouterAdapter impl under
packages/next/src/adapters/. Both are framework bridges, not utilities.
Separates router contracts from cookie contracts:
- adapters/router.ts: RouterAdapterComponent, RouterAdapterRouter, LinkAdapterProps
- adapters/cookies.ts: CookieStore, CookieOptions
- adapters/index.ts: barrel re-export

Makes room for ServerAdapter to land as adapters/server.ts on its own
branch without re-mingling concerns in a single file.
…-abstraction

Adopts the adapters/ folder split for payload core. Resolves the conflict on
admin/adapters.ts by extracting ServerAdapter into adapters/server.ts and
updating the two ServerAdapter consumers (config/types.ts, types/index.ts).
@jacobsfletch jacobsfletch marked this pull request as ready for review May 28, 2026 13:46
r1tsuu
r1tsuu previously approved these changes May 28, 2026
Base automatically changed from feat/admin-router-adapter to main May 28, 2026 15:12
@jacobsfletch jacobsfletch dismissed r1tsuu’s stale review May 28, 2026 15:12

The base branch was changed.

@jacobsfletch jacobsfletch requested a review from AlessioGr as a code owner May 28, 2026 15:12
@jacobsfletch jacobsfletch changed the title feat: framework-agnostic server APIs feat!: admin server adapter May 28, 2026
@jacobsfletch jacobsfletch merged commit ac6f86c into main May 28, 2026
331 of 334 checks passed
@jacobsfletch jacobsfletch deleted the refactor/server-adapter-abstraction branch May 28, 2026 16:16
jacobsfletch added a commit that referenced this pull request May 29, 2026
Requires #16763 and #16753.

Moves all elements and templates defined in the `@payloadcms/next`
package to `@payloadcms/ui`. This is in effort to convert the
`@payloadcms/next` package into a framework adapter.

**Relocated exports** — code physically moved from `@payloadcms/next` to
`@payloadcms/ui`:

| Component | Old source | New source |
| -------------------------- | ----------------------------- |
---------------------- |
| `DefaultTemplate` | `@payloadcms/next/templates` |
`@payloadcms/ui/rsc` |
| `DefaultTemplateProps` | `@payloadcms/next/templates` |
`@payloadcms/ui/rsc` |
| `MinimalTemplate` | `@payloadcms/next/templates` |
`@payloadcms/ui/rsc` |
| `MinimalTemplateProps` | `@payloadcms/next/templates` |
`@payloadcms/ui/rsc` |
| `DocumentHeader` | `@payloadcms/next/rsc` | `@payloadcms/ui/rsc` |
| `DefaultNav` | `@payloadcms/next/rsc` | `@payloadcms/ui/rsc` |
| `Logo` | `@payloadcms/next/rsc` | `@payloadcms/ui/rsc` |
| `HierarchyTypeFieldServer` | `@payloadcms/next/rsc` |
`@payloadcms/ui/rsc` |
| `DefaultNavClient` | `@payloadcms/next/client` | `@payloadcms/ui` |
| `HierarchyTypeField` | `@payloadcms/next/client` | `@payloadcms/ui` |
| `NavSidebarToggle` | `@payloadcms/next/client` | `@payloadcms/ui` |
| `NavWrapper` | `@payloadcms/next/client` | `@payloadcms/ui` |

This PR also removes the now unneeded export aliases. These were in
place for backwards compatibility during v3. Now, we enforce that all
imports point to its canonical source, not the aliased export.

| Component | Old source | New source |
| -------------------------- | ------------------------- |
--------------------- |
| `CollectionCards` | `@payloadcms/next/rsc` | `@payloadcms/ui/rsc` |
| `SlugField` | `@payloadcms/next/client` | `@payloadcms/ui` |
| `QueryPresetsAccessCell` | `@payloadcms/next/client` |
`@payloadcms/ui` |
| `QueryPresetsColumnField` | `@payloadcms/next/client` |
`@payloadcms/ui` |
| `QueryPresetsColumnsCell` | `@payloadcms/next/client` |
`@payloadcms/ui` |
| `QueryPresetsGroupByCell` | `@payloadcms/next/client` |
`@payloadcms/ui` |
| `QueryPresetsGroupByField` | `@payloadcms/next/client` |
`@payloadcms/ui` |
| `QueryPresetsWhereCell` | `@payloadcms/next/client` | `@payloadcms/ui`
|
| `QueryPresetsWhereField` | `@payloadcms/next/client` |
`@payloadcms/ui` |

The `./client`, `./rsc`, and `./templates` subpath exports on
`@payloadcms/next` are removed entirely.

#### Codemod

To migrate automatically, there's a codemod for this change available by
running:

```bash
npx @payloadcms/codemod --transform migrate-next-subpath-exports
```
r1tsuu added a commit that referenced this pull request Jun 2, 2026
Introduce `@payloadcms/tanstack-start`, a framework adapter that lets
the Payload admin run on TanStack Start in addition to Next.js.

What's included:

- `packages/tanstack-start/` — the adapter package: `RouterAdapter`,
  `ServerAdapter`, auth server functions, server-function dispatcher,
  Vite plugins, layouts, and RSC entry points.
- `tanstack-app/` — minimal test harness app that boots the adapter for
  local dev and e2e runs.
- `test/adapters/tanstackStartDevServer.ts` — dev-server module the
  shared test runner will dispatch to when `PAYLOAD_FRAMEWORK=tanstack-start`.
- `dev:tanstack` / `test:e2e:tanstack[:headed]` npm scripts and
  `@payloadcms/tanstack-start` tsconfig path mapping.
- Tanstack-specific plan docs under `docs/plans/`.

This PR only contains TanStack-specific files. The framework-agnostic
abstractions that this adapter consumes (router/server contracts, the
move of admin views to `@payloadcms/ui`, `ComponentRenderer`,
`renderAdminPage` dispatcher, RSC Flight unification, dev-server test
harness abstraction) ship as separate upstream PRs — three of which
(#16753, #16763, #16765) are already merged. See
`docs/reviews/tanstack-pr-shrink-strategy.md` for the full layering and
filing order.

The branch will not build standalone until the remaining abstraction-
layer PRs land upstream.
r1tsuu pushed a commit that referenced this pull request Jun 3, 2026
Decouples the root layout from Next.js-specific implementation details.
This way we can consume the root layout using alternative React
frameworks, e.g. TanStack.

The root layout consumes the new [Router
Adapter](#16763) and [Server
Adapter](#16753), with
additional layers of abstraction for font loading and dependency
checking.

The framework-agnostic implementation lives in `@payloadcms/ui` and is
exposed at a new `@payloadcms/ui/layouts` entrypoint.
`@payloadcms/next/layouts` continues to work unchanged — it is now a
thin wrapper that injects the Next.js adapters, fonts, and dep-check
constraints into the ui implementation, so existing app callers need no
changes.

```tsx
import { RootLayout } from '@payloadcms/ui/layouts'

<RootLayout
  config={config}
  importMap={importMap}
  fonts={[{ className: inter.className, variable: inter.variable }]}
  RouterAdapter={MyRouterAdapter}
  serverAdapter={myServerAdapter}
  serverFunction={serverFunction}
  additionalDependencyChecks={{
    dependencyVersions: {
      'my-framework': { required: false, version: '>=1.0.0' },
    },
  }}
>
  {children}
</RootLayout>
```

Internally:

- `initReq` moves to `@payloadcms/ui/utilities/initReq` and accepts the
adapter as an explicit argument rather than reading `next/headers`
directly.
- Universal `react` / `react-dom` version checks move to
`@payloadcms/ui/utilities/checkDependencies`. Each framework adapter
layers its own constraints through the `additionalDependencyChecks` prop
— the Next.js wrapper passes `next: '>=15.0.0'`.
- The Next.js wrapper now lives at
`packages/next/src/adapters/layout.tsx`, mirroring the existing
`router.tsx` and `server.ts` adapter files.
- Language switching now dispatches through the existing
`serverFunction` mechanism via a new `switch-language` handler in
`@payloadcms/next/utilities/switchLanguageHandler`, rather than as a
dedicated `switchLanguageServerAction` prop. The handler writes the
cookie using the server adapter via `req.server.setCookie(...)`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants