Skip to main content

Registry

ZUI components ship two ways. Both give you the same code; they differ in who owns it afterwards.

npm or registry

npm (@zuilib/components)Registry (source)
Installpnpm add @zuilib/components @zuilib/tokensnpx @zuilib/cli add button
Importimport Button from '@zuilib/components/button'import Button from '@/components/zui/button'
Code lives innode_modulesyour repo (components/zui/)
Upgradesbump the versionzui update and review the diff
Customisationoverride tokens on :root or a subtreeoverride tokens, or edit the file
Build requirementnone (zui.css is prebuilt)Tailwind v4 in your app

Pick npm when you want a dependency: one line to upgrade, no code to maintain, styling through the token contract only. Pick the registry when the component must become yours: you intend to change its markup or behaviour, or your organisation forbids runtime UI dependencies. Mixing is fine. A project can install @zuilib/tokens from npm and pull only the components it wants to fork from the registry.

Only @zuilib/components is in the registry (with the token css it depends on). @zuilib/form, table, toast and text-editor are npm packages.

Set up

Your project needs Tailwind v4. Run once, in the project root:

npx @zuilib/cli init

That writes zui.json, installs the token css and the stylesheet entry under styles/zui/ (src/styles/zui/ when the project has a src/ folder) and adds one line to your global stylesheet, after @import "tailwindcss":

@import "tailwindcss";
@import "./styles/zui/zui.css";

Dark mode is the dark class on <html>, exactly as with the npm package. init guesses the directories; --components, --styles, --css override them, and everything is editable in zui.json afterwards:

{
"registry": "https://zuilib.com/r",
"version": "latest",
"components": "src/components/zui",
"styles": "src/styles/zui",
"css": "src/index.css"
}

Set version to a release ("0.1.0") to pin: every add and update then reads /r/v/0.1.0/ and nothing moves until you change it.

Add a component

npx @zuilib/cli add button

That writes components/zui/button.tsx, then follows the item's registryDependencies: the helpers it imports land in components/zui/lib/ (the same tree as the registry, so the relative imports in the source work unchanged), and npm dependencies the component needs (@headlessui/react, clsx, tailwind-merge, @floating-ui/react) are installed with your package manager at the versions ZUI is built against. Several items in one go:

npx @zuilib/cli add input select dialog

Every write is recorded in zui.lock.json with the file's sha256, which is what makes the next two commands possible.

Keeping up to date

npx @zuilib/cli list # ✓ installed, ↑ newer in the registry
npx @zuilib/cli diff # unified diff, installed files vs registry
npx @zuilib/cli update # rewrite files the registry changed

update rewrites a file only when your copy still matches what the CLI last wrote. A file you edited is reported and left alone; zui diff <item> shows both sides, zui update --force takes the registry's version. Keeping your changes in tokens (:root overrides) rather than in the component files keeps those conflicts rare.

What is in the registry

ItemTypeContents
<component> (button, listbox, table, …)componentOne component file, target components/zui/<name>.tsx
lib-<helper> (lib-cn, lib-control-frame, …)libOne shared helper, target components/zui/lib/<helper>.ts
zui-tokensthemeThe token contract: light and dark custom properties, keyframes, animations, the Tailwind @theme mapping and base styles
zui-stylesthemeThe stylesheet entry (styles/zui/zui.css) plus the component css Tailwind cannot express

The registry is static json, no server behind it:

UrlContents
/r/latest/index.jsonEvery item: type, version, description, npm dependencies, registry dependencies, file targets
/r/latest/button.jsonOne item with its file contents and their sha256; an item can be inspected without installing it
/r/v/<version>/…The same, frozen per @zuilib/components release
/r/schema.jsonJSON schema of the index and item documents

Tokens alone, for a project that only wants the design contract:

npx @zuilib/cli add zui-tokens

For coding agents: llms.txt

The site publishes two files in the llms.txt format so an agent can learn the library without crawling the docs:

  • /llms.txt: the site map. One line per doc page, one line per component with its import line and purpose, one line per helper and theme item, each linking to its registry json.
  • /llms-full.txt: the full component API reference (setup, import rules, every component's props, slots and conventions) and the complete token contract, in one file.

Point an agent at https://zuilib.com/llms.txt in its instructions (CLAUDE.md, AGENTS.md, .cursorrules) and it can pick the right component, write the correct import and know which tokens to override. Give it llms-full.txt when it needs prop-level detail offline. An agent with shell access installs components the same way you do: npx @zuilib/cli add <item>.

Building the registry

Everything under /r/, llms.txt and llms-full.txt is generated from packages/components/src, packages/tokens/src and the components README by packages/components/scripts/build-registry.mjs. It resolves each component's relative imports into registryDependencies and its package imports into dependencies, hashes every file, writes /r/latest/ and /r/v/<version>/, and fails when an import cannot be matched to an item. Run it from the repo root:

pnpm registry:build

pnpm build and pnpm docs:build run it too, so the deployed site never carries a stale registry. Deploying the registry is deploying the docs site.