Documentation

Installation

Install typed Payload CMS blocks with the Payload Components CLI. It wires each block, regenerates Payload types, and updates the admin import map.

Use the Payload Components CLI to add a typed block to a supported Payload CMS v3 project using Next.js App Router. One command installs the block's source files and dependencies, then registers it in the Pages layout and RenderBlocks renderer.

After the block is wired, the CLI runs generate:types and generate:importmap. This refreshes your Payload types and admin import map before the install is recorded in .payload-components/state.json.

Requirements

The installer verifies the target project shape before it mutates anything. If a requirement is missing, it stops rather than leaving a half-wired repo.

Your project must run these majors:

DependencyVersion
payloadv3
nextv15 or v16

…and it must already contain the website-style block layout the installer patches into:

components.json
payload.config.ts
RenderBlocks.tsx
index.ts

Plus package.json scripts for generate:types and generate:importmap, which the CLI runs after wiring.

Install a component

Choose an installable block from the full component catalog, then run the block's add command from the root of your Payload project. Every component page shows its exact command; examples:

npx payload-components add hero-basic

Preview an install before making any changes:

npx payload-components add hero-basic --dry-run

The dry run performs the normal project and dependency validation, then lists the component files, RenderBlocks.tsx renderer mapping, Pages/index.ts block registration, dependencies, and generation commands. It does not write files, install packages, run commands, or update .payload-components/state.json.

Optional draft demo

Demo content is opt-in. Add --demo to a normal install to write a runnable TypeScript seed script strictly after the component install succeeds:

npx payload-components add hero-basic --demo
pnpm exec payload run payload-components/seed-hero-basic.ts

If the component is already installed, write the same script without rerunning the installer:

npx payload-components seed hero-basic
pnpm exec payload run payload-components/seed-hero-basic.ts

The first command only writes payload-components/seed-hero-basic.ts; it does not connect to your database. The CLI also prints the equivalent payload run command for npm, Yarn, Bun, or pnpm. Run that generated script yourself from the project root against the database you intend to modify.

The seed command fails closed

seed requires a current installed-state record, compatible dependencies, every manifest and registry-dependency file, and every Payload layout and renderer fragment before writing the script. If any check is incomplete or stale, it exits non-zero, reports why, and writes nothing. Fix the install with npx payload-components add hero-basic, then retry.

The generated file begins with a versioned ownership marker. Regeneration uses an atomic sibling-file rename and refuses to overwrite an unmarked file, a symlink present during generation, a non-file, or any path outside the detected project. This keeps an existing consumer seed from being mistaken for CLI-owned output. The CLI also creates a mode-0600 private ownership record at .payload-components/demo-state/hero-basic.json. Keep that file with the project: deleting or changing it intentionally makes the generated script refuse to touch existing demo content.

For hero-basic, the generated document contract is deliberately specific:

  • Page slug: payload-components-demo-hero-basic
  • Page title: Payload Components demo — Hero Basic
  • first block marker prefix: payload-components:demo:hero-basic
  • generated media marker prefix: payload-components:demo:hero-basic:media
  • private ownership: a random token, per-create operation tokens, and the exact Page and Media IDs

The script requires the detected Pages collection to support drafts. It aborts before querying or mutating content otherwise. A successful run writes _status: 'draft', so you must review and publish explicitly. On first run, an existing same-slug Page is a collision unless it is the single exact result of an interrupted, locally journaled create—matching public title or a generic marker is not enough. Before creating, the script atomically records a unique operation token. After creation, it saves the returned Page ID. A rerun updates only that exact ID after verifying the expected slug, title, block type, and tokenized marker; the update also uses overrideLock: false. If the process stopped after the database commit but before the ID save, the next run adopts only the single document with the exact journaled token. Missing or mismatched local state causes a hard failure before unsafe mutation.

For components with required uploads, the script journals the Media create, creates one ownership-marked placeholder Media document, immediately saves its returned ID, and reuses only that exact ID on a rerun. If the Media ID save or Page write fails, the journaled operation or saved Media ID makes a retry idempotent. Marker-like or duplicate Media without the matching private record causes a refusal; the generated script never deletes Media. Placeholder files use a unique OS temporary directory that is removed in a finally block. All mutations use the same revalidation-safe context as the project smoke path. The operator-run script uses overrideAccess: true intentionally, so inspect the generated file and your environment before executing it.

What the CLI does

The wrapper CLI owns the Payload-specific work that direct registry installation cannot handle:

Validate the target project shape — refuse to touch a repo that isn't a supported Payload v3 + Next.js project.

registry-build builds the checked-in manifest, then registry-add adds the registry item and installs its source files.

dependency-install installs missing component dependencies so the block compiles in your project.

fragment-apply registers the block in RenderBlocks and the Pages layout field.

post-install runs (generate:types, generate:importmap) and writes .payload-components/state.json.

When --demo is present, writing the demo script is a final sixth action after the installed state above. A failed install never writes a demo script.

Doctor

Run a read-only health check before or after installing components:

npx payload-components doctor

doctor validates the supported project shape, required post-install scripts, and recorded component installs in .payload-components/state.json. It exits non-zero when files, Payload fragments, dependency declarations, or install state need attention.

Already have a block file that is not appearing in the editor or on the page? Use the four-step Payload block troubleshooting checklist to match the symptom to the missing registration, render map, type, or import-map step.

TypeScript cannot resolve @/payload-types, or a new block interface is missing? Use the Payload 3 generated-types repair guide to check the output path, registration order, and generation command.

Want to see how one real block moves from config to the editor and frontend? Follow the hero-basic implementation from Block config through live rendering.

Idempotency

Running the same install twice should not duplicate imports, block registrations, or layout entries. The installer verifies files, fragments, dependencies, and recorded state before deciding whether any work is needed.

Direct registry installs

Every block is also published to a public, shadcn-compatible registry, so you can pull a block's files with the plain shadcn CLI — useful for inspecting a block or dropping one into a non-Payload project.

Register the namespace once in your project's components.json:

{
  "registries": {
    "@payload-components": "https://www.payload-components.xyz/r/{name}.json"
  }
}

Then add any block by name:

pnpm dlx shadcn@latest add @payload-components/hero-basic

Or install a single item straight from its URL, without configuring the namespace:

pnpm dlx shadcn@latest add https://www.payload-components.xyz/r/hero-basic.json

A direct shadcn add only copies the block's source files and its shadcn UI dependencies. It does not register the block in RenderBlocks, wire the Pages layout field, install Payload dependencies, or run generate:types and generate:importmap. For a real Payload project, use payload-components add — it does all of that and records install state.

On this page