feat(tanstack-start): collapse admin root integration to a single shell touch point#17037
Conversation
…ll touch point Adds `withPayloadRoot`, an HOC for a TanStack Start root route's `shellComponent` that renders Payload's admin document shell on `/admin` routes and the consumer's own shell everywhere else. This removes the three prior integration touch points (root loader, loader subscription, manual html threading) in favor of one: shellComponent: withPayloadRoot(MarketingHtml) `PayloadAdminShell` owns the admin `<html>` chrome (no-flash theme bootstrap script, `@layer` ordering, HeadContent, Scripts), and `THEME_INIT_SCRIPT` / `buildThemeInitScript` set `data-theme`/`lang`/`dir` from the theme/lng cookies before first paint — replacing the server-side root theme loader. All exported from `@payloadcms/tanstack-start/client`. Updates the tanstack test app to the new shape: thin `MarketingHtml` shell, `HydrationMarker` extracted and moved into the `_payload` layout, and the now-unused root theme loader removed.
|
Pull Request titles must follow the Conventional Commits specification and have valid scopes. Unknown scope "tanstack-start" found in pull request title "feat(tanstack-start): collapse admin root integration to a single shell touch point". Scope must match one of: cpa, claude, codemod, db-*, db-d1-sqlite, db-mongodb, db-postgres, db-vercel-postgres, db-sqlite, db-d1-sqlite, drizzle, email-*, email-nodemailer, email-resend, eslint, evals, graphql, kv, kv-redis, live-preview, live-preview-react, live-preview-vue, next, payload-cloud, plugin-cloud, plugin-cloud-storage, plugin-ecommerce, plugin-form-builder, plugin-import-export, plugin-mcp, plugin-multi-tenant, plugin-nested-docs, plugin-redirects, plugin-search, plugin-sentry, plugin-seo, plugin-stripe, richtext-*, richtext-lexical, sdk, skills, storage-*, storage-azure, storage-gcs, storage-r2, storage-uploadthing, storage-vercel-blob, storage-s3, translations, ui, templates, examples(/(\w|-)+)?, deps. |
📦 esbuild Bundle Analysis for payloadThis analysis was generated by esbuild-bundle-analyzer. 🤖
Largest pathsThese visualization shows top 20 largest paths in the bundle.Meta file: packages/next/meta_index.json, Out file: esbuild/index.js
Meta file: packages/payload/meta_index.json, Out file: esbuild/index.js
Meta file: packages/payload/meta_shared.json, Out file: esbuild/exports/shared.js
Meta file: packages/richtext-lexical/meta_client.json, Out file: esbuild/exports/client_optimized/index.js
Meta file: packages/ui/meta_client.json, Out file: esbuild/exports/client_optimized/index.js
Meta file: packages/ui/meta_shared.json, Out file: esbuild/exports/shared_optimized/index.js
DetailsNext to the size is how much the size has increased or decreased compared with the base branch of this PR.
|
`startsWith(adminRoute)` matched sibling paths like `/admin-root`. Match exact `/admin` or `/admin/` prefix instead.
688c4d0
into
experiment/framework-adapter-pattern
Reduces the Payload and TanStack Start integration to a single touch point: a shell component attached to the root route.
Now, we attach a single
shellComponentto the root route:With this setup, each shell manages its own HTML tree, and Payload-specific implementation details are completely abstracted away.
Behind the scenes the
withPayloadRootHOC wraps the consumer's own root document shell. Within/adminroutes it renders Payload's admin document shell; everywhere else it renders the consumer's shell.Background
We have to remove as many developer touch points as possible, so that installing Payload into your TanStack app is as few steps as possible with clearly defined abstractions that hide away Payload-specific implementation details.
Underpinning this: in TanStack Router, the root route is shared with your public-facing site, so every Payload touch point is exposed to your marketing shell — complicating setup and inviting drift.
This is different from Next.js, which supports multi-root layouts. This setup is very nice, specifically because it allows you to simply "drop-in" a set of static files into your existing app, and it just works.
Collapsing the touch points into one owned abstraction keeps your public site free of Payload concerns and moves the wiring into
@payloadcms/tanstack-start, where it is versioned once — nothing is left in the consumer's app ot manage.Previously a consumer had to wire up 3 specific things:
Before