Working with components
The copy-in ownership model end to end — add, verify, customize, and pull updates tracked by content.
Last updated
VegaStack components are copy-in: shadcn add writes real source files into
your repo and you own them. Nothing updates behind your back; updates are pulled
when you decide. This page is the full lifecycle.
The five item kinds
| Kind | Example | Behavior |
|---|---|---|
Component (registry:ui) | @vegastack/button | copied to components/ui/, update-tracked by content |
Hook (registry:hook) | @vegastack/use-media-query, @vegastack/use-mobile, @vegastack/use-list-nav, @vegastack/use-platform, @vegastack/use-drag-reorder, @vegastack/use-file-drop, @vegastack/use-animation-replay, @vegastack/use-modal-inert | plain .ts into components/ui/, same tracking |
| Icon item | @vegastack/icon-arrow-right | one animated-icon mirror per item — 467 of them; bare names never collide with components |
Block (registry:block) | @vegastack/dashboard-01 | a multi-file starter composition (page + components + data) you own outright — deliberately NOT update-tracked |
Lib (registry:lib) | @vegastack/geo-data, @vegastack/drag-item | plain .ts into your lib/ alias — data and class-string recipes with no React in them, pulled in automatically as a dependency of the components that read them, and installed once however many of those you add |
Adding
pnpm dlx shadcn@latest add @vegastack/dialog- Always the
@vegastack/prefix. A bareshadcn add dialoginstalls shadcn's own Radix-based dialog — a different component tree that will not match this system (and overwrites files if names collide). registryDependenciescome transitively: adding@vegastack/fieldalso bringsinput;data-listbringstable+checkbox+skeleton+empty;providerbringstoast; the modal families bring the internaluse-modal-inertfocus-containment hook. You never chase dependencies manually or call that modal lifecycle hook yourself.- npm dependencies of each item (
@base-ui/react,class-variance-authority, …) are installed by the CLI into your project automatically.
Every registry item carries a provenance header in its source:
// @vegastack dialog@0.1.0 sha256-Qm3…=Heads-up: the current shadcn CLI strips leading comments during copy-in, so
the header usually won't appear in your copied file — that's fine. Update
tracking does not depend on it: check-updates identifies your copies by
filename against the registry index and compares alias-normalized content.
If a header is present (older CLIs, or files synced by other tooling), it's
used as a fast-path version pin — keep it in that case.
Verifying integrity (recommended for CI-critical repos)
The registry signs its item manifest (Sigstore, GitHub OIDC identity) and every
item carries a whole-item SHA-256. The vegastack-design CLI (a bin inside
@vegastack/design — already installed) closes the fetch-time/copy-time gap:
# 1. verify the item BEFORE copy-in, save the verified bytes
VEGASTACK_VERIFY_DIR="$(mktemp -d "${TMPDIR:-/tmp}/vegastack-verify.XXXXXX")"
VEGASTACK_ITEM="$VEGASTACK_VERIFY_DIR/dialog.json"
pnpm exec vegastack-design verify --save "$VEGASTACK_ITEM" dialog
VEGASTACK_EXPECTED_INTEGRITY="$(node -e \
'process.stdout.write(JSON.parse(require("node:fs").readFileSync(process.argv[1], "utf8")).meta.integrity)' \
"$VEGASTACK_ITEM")"
# 2. copy in
pnpm dlx shadcn@latest add @vegastack/dialog
# 3. prove the files on disk match the verified bytes (alias-rewrite aware)
pnpm exec vegastack-design verify --post-write --item "$VEGASTACK_ITEM" \
--expected-integrity "$VEGASTACK_EXPECTED_INTEGRITY" --target-dir .The save path must not already exist. The private directory prevents pre-creation or symlink redirection; retaining the digest before copy-in also detects replacement of the saved item.
--hash-only skips the Sigstore signature check for local development when
cosign isn't installed; full mode verifies the signed manifest identity too.
Customizing
Edit the copied files freely — that's the point of the model. Two practices keep future updates painless:
- Additive edits over rewrites. Add variants/classes/props; avoid reshuffling
the file wholesale, so
--diffoutput stays reviewable. - Compose in new files where possible — wrap
Buttonin your ownSubmitButtonrather than forkingbutton.tsxfor one use case.
Customized files show as ≈ differs from registry — the content comparison
can't tell your edits from an upstream change; add --diff is what disambiguates.
CI note: --fail-on-update treats that status as failure too, so in-place
customizations keep the drift gate red. If you customize, prefer wrapper files —
or scope the gate: check-updates --fail-on-update --filter <uncustomized,...>.
Updating — pulled, never pushed
The authenticated internal runbook documents how registry pulls relate to npm bumps, including the two-speed propagation model and token-ordering trap for VegaStack-owned applications.
pnpm exec vegastack-design check-updates ⬆ button 0.1.0 → 0.2.0 update available
≈ card → 0.2.0 differs from registry (update or local edits — review with --diff)
✓ field 0.2.0 up to date
? legacy 0.1.0 not in registry (renamed/removed)Status is by content, not version numbers: headered files compare their
integrity hash; headerless files (the normal real-CLI case) compare
alias-normalized bytes against the live item. Either way a component reads
up to date when the registry's global version bumped but that component's
content didn't change — you only re-pull what actually moved. The ≈ differs
status means the bytes diverged — an upstream update or your local edits;
add --diff shows which.
Per component, review then take:
pnpm dlx shadcn@latest add @vegastack/button --diff # see exactly what changed
pnpm dlx shadcn@latest add @vegastack/button --overwrite # take it--overwrite replaces the file — if you customized it, git diff afterwards and
re-apply your edits (they're in your git history; nothing is lost).
Useful flags: --dir (scan a non-default location), --filter <name>, --json
(machine-readable — the right entry point for agents), --fail-on-update (exit
non-zero when anything is stale — the CI drift gate), --registry / --cwd.
Credentialed requests are pinned to VEGASTACK_TRUSTED_REGISTRY_ORIGIN (production by
default) and reject redirects; checkout-local dotenv files cannot redefine that trust anchor.
For agents: the reliable loop is
vegastack-design check-updates --json→ for each item with statusupdateordrift,shadcn add @vegastack/<name> --diff→ present the diff → only after approval--overwrite. Never hand-edit a provenance header, and never "update" by pasting source from elsewhere — pull through the CLI so content and registry stay comparable.
Blocks are different — by design
dashboard-01 copies once and is then yours; check-updates will not track
its page/data files. Rationale: a starter composition you've built a real page on
top of must never be at risk of an upstream overwrite. Its underlying
components (Card, Chart, Sidebar, …) remain individually tracked like any
other.
Agent skills
Install the VegaStack agent skills so Claude Code and Codex build on-system by default — what each one covers, and how to install and update them.
Assembling a multi-step form
Compose Stepper with Field, per-step validation, and async advance gating — the wizard recipe the components deliberately don't own.