Architecture
The repository is split between a Fumadocs site and Payload-targeted component tooling.
Payload Components is two projects living in one repository — and they deliberately never share a Payload runtime.
Site runtime
A Fumadocs + Next.js app: the landing, these docs, and the component catalog. Boots
with no database, no PAYLOAD_SECRET, and no admin.
Component runtime
The Payload block code the CLI installs into your repo. It only ever runs there, against real Payload project shapes.
The two responsibilities are:
- Run a documentation and catalog site.
- Build, validate, and distribute Payload component installs.
Those responsibilities should not share a Payload runtime.
Site runtime
The site runtime is Fumadocs on Next.js. Everything it needs lives in the app tree:
src/appcontains the app router routes.content/docscontains MDX documentation.source.config.tsdefines the Fumadocs MDX collection.src/lib/source.tsexposes the loaded docs source.src/app/api/search/route.tsexposes Fumadocs search.
No Payload admin, collection config, global config, database adapter, or Payload local API is required for the site to boot.
AI-readable surfaces
The site exposes the same docs in browser and machine-readable formats:
/llms.txtpublishes the concise source map for AI crawlers./llms-full.txtpublishes compiled documentation and blog bodies./llms.mdx/docs/**/content.mdpublishes each docs page as markdown./feed.xmlpublishes source-dated blog updates as RSS.- JSON-LD marks the site, CLI, FAQ, component catalog, blog, posts, and docs articles for structured extraction.
AI discovery and verification documents the evidence model, ownership, validation commands, and external maintainer checks for these surfaces.
Component runtime
The component runtime is external. A component installs into a supported Payload v3 + Next.js project, where the Payload-specific files actually live:
- block configs
- frontend block components
RenderBlocksregistration fragmentsPagescollection layout fragmentsgenerate:typesandgenerate:importmappost-install tasks
CLI and database boundary
payload-components add <component> owns five idempotent install stages in the
consumer project: registry build, registry add, dependency install, Payload
fragment application, and post-install generation. It records installed or
partial state under .payload-components/state.json so an interrupted install
can be inspected and retried safely.
Optional demo seeding stays outside that pipeline's trust boundary. The
seed <component> command, and the final --demo step after a successful add,
validate the recorded install, dependencies, registry dependencies, installed
files, and fragments. They atomically write
payload-components/seed-<component>.ts and initialize a private high-entropy
ownership record under .payload-components/demo-state/. The CLI does not
import Payload, open a database, or make a network request during seed
generation.
The operator reviews that generated source and explicitly runs it with the
consumer project's Payload CLI. Only then does the script use the Local API. It
requires Pages drafts and refuses database-visible markers as ownership proof.
It creates a draft only when the slug is free, then records the new Page and
Media IDs locally. Reruns require those exact IDs and the private tokenized
markers; updates set overrideLock: false, and generated code never deletes
media. This keeps elevated overrideAccess calls visible in operator-run target
code instead of hiding them inside the distribution CLI.
Registry source
The shadcn-compatible registry reads source files from payload-components/source and emits public JSON to public/r.
That keeps Payload-specific component source out of the Fumadocs app tree while preserving install targets like ~/src/blocks/HeroBasic/Component.tsx.
Component families and shared code
A component family ships one installable component per structural variant — logo-cloud-grid, logo-cloud-marquee, logo-cloud-inline — each its own registry item and manifest. Variant selection is the catalog and the install command, never an interactive prompt. Content-level variation (optional or extra editor fields) lives in the Payload block config, not a separate component.
Variants that share fields or markup reference a common source file under payload-components/source/blocks/shared, installed once into the consumer repo — for example ~/src/blocks/shared/logoCloudFields.ts — and composed by each variant config as fields: [...logoCloudFields, /* variant-specific */]. Editing the shared file updates every installed variant; re-running an install never overwrites an edited copy. Internal shared modules ship as registry files, not registryDependencies (which resolve only public shadcn components).
Why this split matters
A docs site should build without a database, Payload secret, or admin import map. A Payload component should still be tested against real Payload project shapes. Splitting the app runtime from the install target keeps both jobs honest.