A registry item says what source to deliver. A Payload Components manifest says what must be true after that source arrives. I call it an executable wiring contract because the CLI validates and performs its declarations rather than treating them as descriptive metadata.
The manifest contract has four executable layers: owned files, public dependencies, host-file fragments, and post-install tasks. The CLI records their outcome separately in install state. The declarations say what the repository should contain; the state record says which component, contract version, target, result, error, and patched host files belong to the latest attempt.
Files declare ownership
The manifest lists every source path the component owns in the consumer repository. That list must agree with the shadcn registry item's targets. If a component imports a shared family module, the shared file appears in both contracts too.
Ownership does not mean the installer may overwrite arbitrary edits. It means the component knows which files it delivered and can check whether they are present during diagnosis. Updates and conflicts still need a transparent policy. Host files such as a Pages collection are not owned; they are patched through fragments because the consumer owns the rest of their content.
This distinction lets doctor report “owned component file missing” separately from “Payload
registration missing.” Those conditions have different fixes.
The shadcn registry guide covers the matching delivery contract and target-path rules.
Dependencies declare external requirements
A component may require packages that do not ship as local source. The manifest records them so the dependency-install stage can use the consumer's package manager. Versions and package names need to align with the shipped imports, registry metadata, documentation, and tests.
Dependencies are not a place to hide core project architecture. Payload and Next.js are target requirements, not packages a block should casually add to an unknown application. The manifest's support metadata describes compatible major versions; project detection verifies them before source mutation begins.
Internal shared modules are files, not dependencies. Public shadcn UI primitives are registry dependencies in the source registry. Keeping these categories separate makes both ownership and failure messages understandable.
Fragments describe scoped host edits
Fragments are the Payload-specific heart of the manifest. A fragment identifies a host file role,
an anchor, an insertion, and deduplication evidence. One fragment can import a block config, another
can register it in layout.blocks, and others can import and map the React renderer.
The CLI does not treat arbitrary text replacement as safe. It resolves the project layout, reads the candidate file, verifies the anchor, checks whether the intended code already exists, and only then applies a narrow insertion. If assumptions do not hold, the stage fails before guessing.
A fragment is declarative enough to inspect in JSON but specific enough to execute. It also becomes
diagnostic evidence: doctor can check whether the same import and registration are still present
later.
The text-anchor article explains why this project accepts a constrained starter shape instead of rewriting unknown TypeScript syntax trees.
Post-install tasks rederive project-wide artifacts
Generated Payload types and the admin import map describe the whole consumer application. They cannot be delivered as component-owned files. The manifest therefore declares the scripts that must run after source and fragments are complete.
Ordering matters. Type generation must see the newly registered block. Import-map generation must see final component paths. Running either earlier can succeed while producing output that omits the new integration.
The CLI invokes commands configured for the detected project rather than assuming generated output
paths. A failed post-install task leaves the component in a partial state with lastError.stage set
to post-install and lastError.message set to the command error. After the underlying command is
fixed, a rerun checks the repository again and performs whatever work is still required.
State records execution, not truth by decree
Each component entry in .payload-components/state.json has exactly the installer fields
manifestVersion, registryItemName, targetId, status, installedAt, lastAttemptAt,
lastError, and patchedFiles. It records results rather than telling a retry where to begin. It is
not allowed to overrule the filesystem. If the manifest expects an owned file and a developer
deleted it, doctor reports the mismatch.
This makes state an index of expected truth rather than truth itself. The installer can compare record and repository, explain discrepancies, and choose a safe next action. A blind “installed: true” flag would be much less useful.
State also enables honest partial installs. A dependency failure after file delivery is neither a
clean rollback nor a successful component. A partial status plus the failed stage and message in
lastError gives the next run useful context without claiming that state knows what remains on
disk. The idempotent installer guide follows the convergence
model in detail.
Schema validation moves errors before mutation
Every manifest is checked against a schema. Required names, versions, target metadata, file entries, fragments, and post-install definitions fail validation before the CLI changes a consumer repository. Cross-contract tests then confirm manifest files agree with registry targets and actual source.
Schema validity is necessary but not sufficient. A fragment can be well-shaped JSON and still name the wrong renderer key. Integration fixtures exercise installation against a supported starter, inspect resulting source, rerun the operation, and simulate failures. That behavioral test is what turns metadata into a contract.
When adding a field to the manifest schema, I ask whether it changes execution or validation. If it does neither, it may belong in documentation instead. Executable metadata should stay small enough that contributors can reason from a manifest to a diff.
Version the contract when behavior changes
A component version should change when its delivered files, required dependencies, or wiring expectations change. The state record can then distinguish an installed older contract from the current manifest. An update strategy must still respect local edits; source ownership is not a license to replace modified files silently.
The current project focuses on deterministic adds and diagnostics. Any future update workflow must
build on the same facts: the prior manifestVersion, the manifest's declared files and fragments,
recorded patchedFiles, current repository content, and a reviewable proposed diff. The manifest
provides the expectations without pretending the merge decision is trivial.
Read a manifest like an install preview
Before running a component, you should be able to answer:
- Which files will appear, and where?
- Which packages or public registry items are required?
- Which host files will receive imports or registrations?
- Which exact discriminators will be inserted?
- Which generators will run?
- What state will be recorded if a stage fails?
The component documentation renders much of this information from the manifest so prose and installation behavior cannot drift. See Hero Basic for the resulting files and wiring table.
Then run the command on a branch and compare the promised contract with the actual diff:
npx payload-components add hero-basicIf they differ, that is a useful issue: include the manifest section, sanitized output, and observed diff. Better still, add a fixture assertion with the correction. The manifest system improves when real installations sharpen its executable promises.



