[![Contributors][contributors-shield]][contributors-url] [![Forks][forks-shield]][forks-url] [![Stars][stars-shield]][stars-url] [![Issues][issues-shield]][issues-url] [![MIT License][license-shield]][license-url] [![npm][npm-shield]][npm-url]
Payload Components
Payload CMS blocks, wired - not pasted.
Payload Components is an MIT, community-first registry and CLI for installing typed Payload CMS blocks into Payload v3 + Next.js projects, with experimental Payload v4 support. It copies the files, wires Payload config, regenerates generated artifacts, and leaves the whole install as a reviewable git diff.
Explore the docs · Browse the catalog · Contribute · Report a bug · Request a component
Table of Contents
About The Project
Payload Components installs Payload CMS blocks into supported Payload v3 + Next.js projects.
| Payload | Next.js | Support |
|---|---|---|
| v3 | 15 or 16 | Fully supported |
| v4 | 16, 16.2.6 or newer | Experimental; needs Node.js 24.15 or newer |
Payload v4 support tracks Payload's prereleases. The CLI refuses a Payload v4
project on Next.js 15, and doctor warns below the Next.js and Node.js minimums.
A plain shadcn add copies files. payload-components add goes further:
- copies component source through the shadcn-compatible registry;
- registers the block in the Pages collection;
- maps the frontend renderer;
- regenerates Payload types and the admin import map;
- records install state so reruns converge instead of duplicating wiring.
This repository has two jobs:
- the Fumadocs-powered Next.js site for the landing page, docs, blog, component catalog, template showcases, search, Open Graph images, and AI-readable text surfaces;
- the
payload-componentsregistry and CLI that install blocks into consumer Payload projects.
This repository is not a Payload CMS runtime app. The docs site does not need
Payload admin routes, collections, globals, a database adapter, or
PAYLOAD_SECRET.
Why It Exists
[Payload blocks][payload-blocks-guide-url] are not live when their files land. They have to be registered, rendered, typed, and added to the admin import map. That wiring is repetitive, easy to drift, and usually rediscovered project by project.
Payload Components packages the block source and the wiring contract together. The goal is a catalog that grows from real installs and pull requests: MIT, open source, no pricing tiers, no license keys, no gated component access.
What Makes This Different
- MIT end to end: registry, CLI, components, and docs site.
- No license keys, gated components, or paid component tiers.
- Community-driven requests and pull requests decide what ships next.
- One command installs source files plus the Payload wiring that makes them live.
Built With
- [Next.js][next-url]
- [React][react-url]
- [Fumadocs][fumadocs-url]
- [Payload CMS][payload-url]
- [shadcn Registry][shadcn-url]
- [TypeScript][typescript-url]
- [Tailwind CSS][tailwind-url]
- [Playwright][playwright-url]
- [Vitest][vitest-url]
What Lives Here
| Path | Purpose |
|---|---|
src/app |
Next.js routes: landing, docs, catalog, blog, templates, search, and OG |
src/components/site |
Site UI, landing sections, install replay, demo twins, template showcases |
src/lib |
Site copy (site.ts), catalog context, template recipes, content sources |
content/docs, content/blog |
Fumadocs MDX documentation and blog posts |
messages |
Site translation catalogs; English is canonical |
payload-components/registry.json |
Source shadcn registry definition |
payload-components/source |
Payload target source files shipped into consumer repos |
payload-components/manifests |
Install metadata, fragments, post-install tasks, recovery |
payload-components/templates |
Generated install contracts for the full-site templates |
tools/payload-components |
CLI implementation for payload-components add |
bin/payload-components.mjs |
CLI entrypoint for working in this repo |
tests |
Playwright E2E and Vitest integration coverage |
rfcs, docs |
Design proposals and dated maintainer records |
AGENTS.md has the full repository map and the contracts each area must keep.
Getting Started
There are two workflows: installing components into a Payload project, and working on this repository.
Use A Component
Run the CLI from the root of a supported Payload v3 (or experimental v4) + Next.js project:
npx payload-components add hero-basic
Preview the same validation and install plan without changing files, installing dependencies, running generation commands, or updating install state:
npx payload-components add hero-basic --dry-run
The preview lists the component files, RenderBlocks.tsx renderer mapping,
Pages/index.ts block registration, package dependencies, and post-install
commands the real install would use.
Good first installs:
| Component | Use it for |
|---|---|
hero-basic |
A headline-led page hero |
feature-grid-basic |
A repeatable feature grid |
feature-split |
A two-column feature section |
content-columns |
Editorial content columns |
logo-cloud-grid |
A trust-logo wall |
integration-grid |
Integration cards |
Browse the full, current set in the [component catalog][catalog-url], or jump to All Components for every install command.
Collection Query supplies grid, list, and featured Posts layouts with category filters and pagination. Contact Form Basic submits to an endpoint you own. Post Hero, Author Card, Newsletter Callout, and Related Posts are file-only article components: compose them in your post template; their installs do not edit Pages or run Payload generators. See the [visual install walkthrough][install-walkthrough-url] for catalog discovery, a representative install, the resulting diff, and doctor.
Run This Repo Locally
Use these steps to run the docs site and registry tooling.
Prerequisites:
- Node.js
^20.19.0 || >=22.12.0 - pnpm
^9 || ^10(packageManagerpins the exact version CI uses)
Install and start:
git clone https://github.com/Ducksss/payload-components.git
cd payload-components
pnpm install --frozen-lockfile --ignore-workspace
pnpm source:build
pnpm dev
Open http://localhost:3000.
Useful local routes:
| Route | Purpose |
|---|---|
/ |
Product and docs homepage |
/docs |
Fumadocs documentation |
/components |
Component catalog |
/templates |
Full-site template showcases |
/blog |
Project blog |
/api/search |
Fumadocs search endpoint |
/llms.txt, /llms-full.txt |
AI-readable project summaries |
/r/registry.json |
Generated public registry |
Before opening a pull request, run the checks that match your change.
CONTRIBUTING.md maps each kind of change to
its focused checks; pnpm test:release runs the full local release gate.
Usage
Run every command from the root of the target Payload project. The [CLI reference][cli-reference-url] documents each command and flag.
Install Several Blocks Or A Template
Install several blocks in one command — add takes any number of names, and the
catalog's composer builds the command for you as you tick components:
npx payload-components add hero-basic faq-card pricing-cards
Install every block a full-site template concept composes, then assemble its pages in the admin:
npx payload-components templates
npx payload-components add-template saas-launch
add-template installs and wires the whole block set and prints which blocks
each page uses. Add --demo to also write one seed script per page, each
creating a draft Page from the blocks that page composes. Seeded content is each
block's own sample content, not the curated copy shown on the site.
Localize A Project
Make the project multilingual — English, Chinese, or any language tag Payload accepts:
npx payload-components localize --locales en,zh
localize sets both halves of Payload internationalization, which have to agree
before an editor sees a locale switcher. It declares the locales in your
buildConfig({ ... }) call (labelled in each language, rtl where the script
needs it), installs src/blocks/shared/localizeFields.ts and wraps every
installed block config's field list in it so the shared family base is covered
too, and records the choice in install state so update keeps the wrapper. Every
step is idempotent — re-run it after installing more blocks, and drop --locales
to keep the ones already declared.
--default-locale picks the canonical locale, --no-fallback lets an
untranslated locale render empty, and --dry-run prints the whole plan without
touching a file. A config it cannot read, a localization block that already
declares something else, and a locally edited block config are all reported and
skipped rather than rewritten; --force overrides the last two. A single block, or
every block of a template, can also be installed localized in one step with
npx payload-components add hero-basic --localized or
npx payload-components add-template saas-launch --localized — the same
field-level transform without the config half.
Turning on localization changes how Payload stores the affected fields, so migrate existing data before adopting it on a populated collection. The [internationalization guide][localization-guide-url] covers locale choice, right-to-left scripts, and front-end queries.
Maintain An Install
Recorded installs have a full lifecycle, not just a first run:
npx payload-components list # catalog vs what this project recorded
npx payload-components diff # version, file, and wiring drift
npx payload-components update # re-install anything behind this CLI
npx payload-components localize # declare locales and localize installed blocks
npx payload-components doctor # project shape, wiring, and recorded installs
npx payload-components remove hero-basic --accept-stored-content # after migrating/removing stored blocks
diff exits non-zero when anything has drifted, so CI can gate on it. update
compares against the source hashes captured at the last successful install and
never overwrites a file you have edited — it skips that component and exits
non-zero until you pass --force. remove protects edited or unrecorded source
unless you pass its own --force, and deletes only files no other recorded
install owns, so a shared family base survives while a sibling variant is still
installed. It also requires --accept-stored-content, because removing code does
not migrate Page documents. Package dependencies are always left in place.
list and diff accept --json, and update and remove accept --dry-run.
add repairs the recorded component version; it never upgrades one. To move to a
newer version, run payload-components update <name>: it validates the selected
components and saves source, host files, package files, and ownership to
.payload-components/update-backup.json before replacing anything, and a failed
update restores that snapshot. If the process was interrupted, preserve any edits
made afterward, then run payload-components update --recover before retrying.
Package installation and custom generator side effects outside the saved paths
may need reconciliation. Unreadable install state fails closed; restore it from a
known-good backup instead of deleting it and losing file ownership.
doctor changes no files. It validates the supported project shape, resolves
which files carry the wiring, and checks required post-install scripts and any
recorded .payload-components/state.json installs.
Start From A Bare Payload App
Starting from a bare create-payload-app project? Lay down the base an install
needs — the Pages and Media collections, the blocks renderer, and the cn /
CMSLink / Media / linkGroup primitives every block imports:
npx payload-components init --scaffold
The base is recorded with content-addressed ownership in install state. Matching
starter files are adopted, existing custom implementations are left unowned, and
later runs update only pristine managed files. Locally edited managed files are
kept until you explicitly pass --force. The result is the official starter's
shape, so the project then detects as payload-website-starter.
Scaffolded Pages collections restrict anonymous reads to published Pages.
Managed, unedited scaffolds from older releases pick up that fix when
init --scaffold runs again; unmanaged or locally edited collections are
preserved. If yours still uses read: () => true, apply the updated callback from
payload-components/source/base/collections/Pages/index.ts to protect drafts.
Seed Demo Content
Demo scripts are opt-in. add <component> --demo writes one after the install
succeeds, and seed <component> writes the same script for a component that is
already installed. Run the generated TypeScript through your project's Payload
CLI; the CLI prints the exact command for your package manager:
npx payload-components add hero-basic --demo
pnpm exec payload run payload-components/seed-hero-basic.ts
Neither command opens a database. seed rejects partial installs and verifies
the recorded install, dependencies, files, and Payload wiring before it writes
the reviewable script plus a private ownership record under
.payload-components/demo-state/. The script requires Pages drafts and creates a
draft Page at /payload-components-demo-hero-basic; it never publishes the
demo. Reruns update only the exact Page and Media IDs recorded locally, an
interrupted run adopts only the document carrying its private operation token,
and placeholder media is never deleted. The script deliberately uses
overrideAccess: true, with overrideLock: false for updates, so review it in
git and run it only against the intended database. [Seed Payload CMS
safely][seed-guide-url] covers the full ownership model.
Use It From A Coding Agent
payload-components mcp runs a Model Context Protocol server over stdio so an
agent can browse the registry, read a component's install contract, and preview
what an install would change:
// .mcp.json / your client's MCP config
{
"mcpServers": {
"payload-components": {
"command": "npx",
"args": ["-y", "payload-components", "mcp"],
},
},
}
Every tool is read-only by design. The server answers which block and what will
it change; installing stays an explicit payload-components add run in your
shell, where the diff is visible and approvable.
How An Install Works
The installer runs five idempotent stages:
- Build or resolve the public registry item.
- Add component files through the shadcn registry.
- Install required dependencies.
- Apply Payload fragments for collection registration and renderer mapping.
- Run post-install scripts for generated types and the admin import map.
Install state is written to .payload-components/state.json inside the
consumer project, so partial installs are visible and retries can converge. A
successful install also records normalized hashes for its owned source files;
diff, update, and remove use that install-time baseline to distinguish an
upstream release from a consumer edit.
Recovering An Interrupted Install
If a stage fails, the component is recorded as partial and payload-components add
prints the failed stage, the last error, and the