Skip to content
UI Brok

Loading…

No results

Browse catalog

Registry Format

The registry is the source of truth for every installable component, block, page, bundle, and theme. Each item is an item.json manifest plus one or more source files living under registry/open/ or registry/pro/. Running php artisan registry:build validates the tree, inlines file content and SHA-256 hashes, and writes JSON endpoints under public/r/<tier>/ that the ui:add CLI fetches and writes into the consumer app.

Manifest

Every item declares itself through an item.json file at the root of its source directory. Below is the real manifest for the Button component.

registry/open/ui/button/item.json JSON
{
    "name": "button",
    "type": "registry:component",
    "title": "Button",
    "description": "Displays a button or link with variants, sizes, loading and an optional animated arrow.",
    "version": "1.2.1",
    "license": "open",
    "stability": "stable",
    "knowledge": {
        "keywords": ["action", "submit", "call to action"],
        "useWhen": ["Triggering an immediate action such as saving or submitting."],
        "avoidWhen": ["Representing a persistent on/off value; use switch or checkbox."],
        "related": ["button-group", "toggle"]
    },
    "registryDependencies": [],
    "dependencies": {
        "composer": ["jml/brok:^1.0"],
        "npm": []
    },
    "files": [
        {
            "path": "ui/button/button.blade.php",
            "target": "resources/views/components/ui/button.blade.php",
            "type": "blade"
        }
    ],
    "meta": {
        "a11y": "wcag-2.2-aa-target",
        "responsive": true,
        "rtl": true,
        "darkMode": true,
        "localized": true
    }
}

Field reference

Field Type Description
name string Slug used by the CLI (ui:add button) and as the registry key. Lowercase, hyphenated.
type string Item kind. One of registry:component, registry:block, registry:page, registry:bundle, or registry:theme. Drives target and catalog validation.
title string Human-readable display name shown in docs and the CLI confirmation prompt.
description string One-sentence summary rendered on the docs index and in ui:status output.
version string Semver string. ui:diff and ui:update compare this against what is recorded in the consumer's ui-lock.json.
contractVersion integer Version of the normalized human/agent contract emitted by the builder. Authored v2 manifests are checked against their Blade prop names and defaults.
foundationVersion semver Minimum shared ui.css/_styles.php foundation required by the item. The CLI blocks incompatible installs.
license string "open" (MIT, no auth required) or "pro" (commercial, requires ui:auth). Must match the tier directory the item lives in; the build enforces parity.
stability string Maturity signal ("stable", "beta", "experimental", or "deprecated"). Change impact is recorded separately in migrations.
registryDependencies string[] Other registry item slugs this item requires. The CLI resolves them recursively (with a cycle guard) and installs them automatically. Open items may not depend on pro items.
knowledge object Guidance for humans and agents: keywords, use/avoid cases, anatomy, source-derived props/slots, behaviour, examples, related items, accessibility, and theming. Sparse authored data is completed by the builder.
composition object Machine-readable layout contract: kind, surface, frame, regions, responsive rules, states, and components used. Blocks and pages expose it through ui:show --skeleton.
migrations[] object[] Structured from/to migration guidance, summary, breaking flag, optional manual steps, and optional codemod identifier.
deprecation object Required when stability is deprecated: version introduced, replacement item, removal horizon, and optional message.
dependencies.composer string[] Composer package constraints the consumer must have in their composer.json. The CLI checks for these and warns if they are absent.
dependencies.npm string[] npm package constraints (e.g. "alpinejs:^3.0") wired into the consumer's package.json check.
files[].path string Path to the source file relative to the tier root (registry/<tier>/), i.e. <kind>/<name>/<file> such as ui/button/button.blade.php. This is the single edit point for that file.
files[].target string Where the file lands in the consumer's project. Must be under resources/views/components/ui, resources/views/blocks, resources/views/pages, resources/js/ui, or resources/css/themes. The build rejects any target outside these paths.
files[].type string File kind: "blade", "js", or "css". JS is wired into resources/js/ui/index.js; registry theme CSS receives a managed import in resources/css/ui.css.
meta object Quality signals: a11y, responsive, rtl, darkMode, localized. Displayed as capability badges in the docs UI.

The build pipeline

php artisan registry:build (the BuildRegistry command in apps/docs-registry) runs in four stages:

  1. Scan — walks every registry/<tier>/<kind>/<name>/item.json and loads the manifest.
  2. Validate — checks that every declared source file exists, targets stay inside the allowed path set, license flags match the tier, and open items carry no pro dependencies.
  3. Inline — reads each source file and embeds its UTF-8 content plus a sha256 hash directly into the published JSON so the CLI can detect transport corruption after download. (The hash is co-shipped with the content, so it guards against accidental drift, not a tampered registry — see the integrity note below.)
  4. Write — emits public/r/<tier>/<name>.json per item and a tier-level public/r/<tier>/registry.json index.

On the consumer side, ui:add <name> fetches the item JSON from the registry endpoint, writes each files[].target into the app, resolves registryDependencies recursively (a cycle guard prevents infinite loops), wires any JS files into resources/js/ui/index.js, and records the installed version in ui-lock.json.

Three places must stay in sync

A registry item is only fully "done" when it exists in all three of the following locations simultaneously. Guard tests (CatalogNavigationTest, MirrorParityTest, BlockRenderTest) enforce parity in CI and fail fast when any location is missing.

1. Registry source

registry/open/<kind>/<name>/ — the item.json manifest plus all source files. This is the single edit point. Never modify files anywhere else to "fix" the component.

2. Docs-app render + JS copy

The docs site renders each item's Blade in place from registry/ via non-prefixed anonymous component paths; there is no per-item Blade mirror. Each item's behaviour JS is copied into the docs app at its files[].target because the lazy loader can only glob resources/js/ui/**. Run php artisan registry:mirror — never hand-copy files.

3. Catalog entry

The kind-specific file under apps/docs-registry/catalog-data/ drives navigation, docs pages, and every catalog-driven guard test. An item that is installable but not listed there is invisible. Block and page render providers derive from the catalog row; do not maintain a second test list.

Note

Mirror registry JS after editing it. After editing a registry item's behaviour JS, run php artisan registry:mirror manually. Drift is gated by php artisan registry:mirror --check, which runs inside composer check and CI and exits non-zero if any shared asset or per-item JS copy diverges.

Validation rules

The build command rejects any manifest that violates the following rules. All checks also run with --dry-run so you can validate without writing output files.

  • Every declared file must exist. Each files[].path is resolved against the item source directory; the build fails if the file is missing.
  • Open items may not depend on pro items. Any slug in registryDependencies is checked against the tier of the dependency; cross-tier references from open → pro are rejected.
  • Targets must stay under allowed paths. Valid prefixes are resources/views/components/ui, resources/views/blocks, resources/views/pages, and resources/js/ui. Any other target path is rejected.
  • Published files carry SHA-256 hashes and inlined content. The build computes a sha256 of each source file and embeds both the hash and the full file content in the published JSON. The CLI recomputes the hash on write and aborts on a mismatch. Because the hash is co-shipped with the content, this catches transport corruption and accidental drift — by itself it is not a defence against a tampered or malicious registry (an attacker who can alter the response can recompute a matching hash), and unpinned consumers rely on HTTPS transport trust to the registry host. For provenance independent of the transport, pin the publisher's Ed25519 key as registrySigningPublicKey in ui.json: both ui:add and ui:update then verify the registry index against its detached signature before trusting anything, failing closed on a missing or invalid signature, reject a replayed older index, and bind every fetched item to the signed index's per-item content hash — a shipped guarantee, not a planned one.
  • License flags must match the source tier. An item under registry/open/ must declare "license": "open"; an item under registry/pro/ must declare "license": "pro".
  • Name must match the directory slug. The name field must equal the item's containing directory name to prevent mismatches between the CLI key and the file path.

Validate locally

Run the build in dry-run mode from the apps/docs-registry directory to catch manifest errors without writing any output files:

terminal
php artisan registry:build --dry-run

To also check that shared assets and per-item JS copies are in sync with their canonical sources (useful before opening a PR):

terminal
php artisan registry:mirror --check

Warning

The composer check shortcut in apps/docs-registry runs registry:mirror --check, Pint, and the full test suite in one command. Run it before pushing to confirm nothing is out of sync.