ui8kit
ui8kit CLI
UI8Kit 3 ecosystem
- github.com/ui8kit/codegen — generator of primitives (spec →
generated/)- github.com/ui8kit/registry — packed per-runtime registry on npm/CDN — npm
@ui8kit/registry- github.com/ui8kit/cli — installer
ui8kit— npmui8kit← you are herePipeline: codegen (spec →
generated/) → registry (packr/<rt>/) → CDN (unpkg / jsDelivr) → cli (init/add) → app
Shadcn-style registry CLI: init / add / list / diff / info / scan / build. It fetches registry items from a CDN and writes source files into your app (not into node_modules), so Tailwind sees the classes.
npm package ui8kit@3.x · registry digest @ui8kit/registry@3 (per-runtime r/<runtime>/) · runtimes react, vue, svelte, solid, webcomponent · config contract 2.0.0
Docs: docs/README.md · Upgrading from 2.x: docs/migration-v3.md · Agents/maintainers: AGENTS.md
Idea
@ui8kit/registry@3/r/<runtime>/ → npx ui8kit@3 init → {dir}/utils/… + .cursor/rules/ui8kit-primitives.mdc
→ npx ui8kit@3 add → {dir}/ui/<brick>/…
Primitives are generated by ui8kit codegen and published per runtime; this CLI installs them.
Requirements
- Node.js
>=20(or Bun) - A project with
package.json(created byinitif missing). Vite/React are not required.
Quick start
npx ui8kit@3 init --yes --framework react --dir src
npx ui8kit@3 add button text box
Primitives in @ui8kit/registry 3.2 (19): box, text, button, link, label, form, input, textarea, select, checkbox, radio, switch, image, icon, dialog, disclosure, meter, progress, separator (+ React-only slot). The CLI has no hard-coded list: add <name> / add --all read the registry index, so new primitives arrive with the registry.
initwritesui8kit.config.json, installsregistry:utilsinto{dir}/utils(plus npm peersclsx,tailwind-merge) and theregistry:rulesitem into.cursor/rules/ui8kit-primitives.mdc({kit}→ your primitives folder,{globs}→ files underdir). Only folders of files actually written are created.addinstalls bricks into{dir}/ui/<brick>/and resolvesregistryDependencies(slot,utils, …).
Other runtimes: --framework vue|svelte|solid|webcomponent (Web Components install *.wc.ts + shared files).
Layout under {dir}
| Path | Role | Registry type |
|---|---|---|
ui/<brick>/ |
primitives (codegen) | registry:ui |
utils/ |
cn, variants, … — required by primitives (../../utils) |
registry:utils |
.cursor/rules/ (app root) |
agent rules for the primitives | registry:rules (installed by init only) |
components/, blocks/, layouts/, lib/, variants/, widgets/ |
other item types (brand registries) | registry:component / block / … |
Remap safely at the same depth (e.g. "paths": { "ui": "kit" }); keep utils at {dir}/utils. Precedence: --path key=dir > config.paths > file target — see Configuration.
Registry version
- Default = CLI major: ui8kit 3.x fetches
@ui8kit/registry@3→…/r/<runtime>/(newest 3.x.y; not pinned). - Pin:
--registry-version 3.0.0or"registryVersion": "3.0.0"(ranges like3.1and dist-tags likecanarywork too). - Server runtimes (templ, latte, twig, jinja2) are first-class in 3.2 (
r/<runtime>/of@ui8kit/registry@3); see Server runtimes.
CDN order: --registry-url / registryUrl → cdnBaseUrls → built-in unpkg / jsDelivr. --strict-cdn + explicit URL → only that URL. URL registryDependencies resolve bare names against the item's own registry base. Details: docs/cdn-and-cache.md.
Server runtimes (templ, latte, twig, jinja2)
npx ui8kit@3 init --yes --framework templ --dir internal # or latte / twig / jinja2 (e.g. --dir templates)
npx ui8kit@3 add button
initwrites onlyui8kit.config.json(typescript: false),ui8kit.lock.json,.cursor/rules/and{dir}/utils(templ),{dir}/runtime(twig/latte) or{dir}/ui8kit_jinja(jinja2). Nopackage.json, no tsconfig;adddoes not require apackage.jsoneither.- Toolchain deps are never installed automatically and never go to npm. After
init/addthe CLI prints the exact commands once (deduped) from the itemmeta:go get github.com/a-h/templ@v…,composer require "twig/twig:^3.0",pip install "Jinja2>=3.1,<4"(double quotes: works in bash, PowerShell and cmd). - templ: generated code imports
github.com/ui8kit/codegen/generated/templ/utils. With ago.modin the app root the CLI rewrites it (in*.templand*.go) to<module>/<utils dir>, e.g.example.com/app/internal/utils. Withoutgo.modthe import is left as is and a warning prints the fix:go mod init <module>+ui8kit add <items> --force, or replace the import by hand. Thentempl generate && go build ./.... - twig / latte: the CLI prints a PSR-4 snippet (
"UI8Kit\\Twig\\": "<dir>/runtime/"/UI8Kit\\Latte\\) for yourcomposer.json; it never editscomposer.json. Runcomposer dump-autoload. Twig:$twig->addExtension(new \UI8Kit\Twig\TwigExtension()). - jinja2:
ui8kit_jinja/sits next toui/;from ui8kit_jinja import environment→environment().get_template("ui/button/button.html.j2").render(p={...}, children=...). *_templ.gois generated: registry ≥ 3.1 (rebuilt) ships only*.templ; runtempl generateafter everyadd(and in CI).checkcovers only files the CLI wrote, so generated*_templ.gonever shows up as drift. Old installs that still have a locked*_templ.gowill show it asmodifiedaftertempl generate— remove those*_templ.gokeys fromui8kit.lock.json, or delete the lock and runui8kit check --adopt(re-creates it from files matching the registry).
Config (ui8kit.config.json)
{
"$schema": "https://cdn.jsdelivr.net/npm/@ui8kit/registry@3/schema/schema.json",
"contractVersion": "2.0.0",
"framework": "react",
"runtime": "react",
"typescript": true,
"dir": "src",
"globalCss": "src/index.css",
"aliases": { "@": "./src" },
"registry": "@ui8kit",
"importStyle": "alias"
}
--import-style package (@ui8kit/core imports) is deprecated: it still works but prints a warning.
For app developers: lock, check, versions
ui8kit.lock.json
init and add record every installed file in ui8kit.lock.json: the registry it came from (source, the URL actually used), the exact version (3.0.0, never a range) and a sha256 per file. Commit it — it is how you (and CI) know the kit on disk is exactly what the registry shipped. Do not edit it by hand.
npx ui8kit@3 check # offline, no network; exit 1 on errors
npx ui8kit@3 check --json # machine-readable report
Add npx ui8kit@3 check to CI (see CI scenario). Existing app installed before 3.1 (no lock): run npx ui8kit@3 check --adopt once — it fetches the registry, hashes what each item would install and adopts only files that match; others are listed as not-adopted (decide: re-install with add <name> --force, or keep as your own file). Commit the lock.
check error |
Meaning | Fix |
|---|---|---|
missing |
a locked file was deleted | npx ui8kit@3 add <name> --force |
modified |
file content differs from what was installed — a local edit, or aliases / importStyle / paths changed after install (import rewrites change content) |
revert the edit (add <name> --force) and move styling to className / tokens; after a deliberate config change re-install the items with --force |
mixed-versions |
primitives from one registry are on different versions (e.g. button 3.0.0, text 3.1.0) |
npx ui8kit@3 add <outdated…> --force (or add --all --force) so all share one version |
rules-version |
.cursor/rules/ui8kit-primitives.mdc is from another version than the primitives |
re-run npx ui8kit@3 init with your flags (refreshes utils + rules), or bring the primitives to the rules version |
Line endings: hashes are computed with CRLF normalized to LF, so git config core.autocrlf true on Windows is not reported as modified.
Registry version
The default registry is the CLI major (ui8kit@3 → @ui8kit/registry@3, newest 3.x.y). To make installs reproducible, pin an exact digest: "registryVersion": "3.0.0" in ui8kit.config.json (or --registry-version 3.0.0). Upgrade deliberately: change the pin, add --all --force, init (rules), check, commit.
Brand registries
A brand registry (e.g. https://registry.buildy.tw/react) ships blocks / composites that depend on the primitives by URL (registryDependencies: ["https://cdn.jsdelivr.net/npm/@ui8kit/registry@3.0.0/r/react/components/ui/button.json"]) and records the digest it was built against in registry.json top-level meta (copied to index.json):
{ "meta": { "primitives": { "registry": "@ui8kit/registry", "version": "3.0.0" } } }
Brand items get their own lock entries (key per registry identity), separate from @ui8kit/registry.
Primitives are not forked. Do not edit files under your kit folder ({dir}/ui, {dir}/utils) — check reports it, and add --force would overwrite it. Brand styling goes through className on the primitives and design tokens (CSS variables / Tailwind theme); new UI goes into your own components or a brand registry.
Commands
| Command | Purpose |
|---|---|
init |
config + utils + agent rules |
add <names…> / add --all |
install items (--runtime, --dry-run, --force, --path key=dir) |
check [--json] [--adopt] |
verify files against ui8kit.lock.json |
list / diff |
browse registry / compare local files (read registry settings from config) |
info [--cdn] |
environment and resolved CDN order |
scan / build |
author a registry: src/registry.json → r/ |
cache clear / registry clean / reset |
maintenance |
Full reference: docs/commands.md.
Development
npm install
npm run type-check
npm test
npm run build
npm run pack:check
Scripts: scripts/README.md. Changelog: CHANGELOG.md.
License
MIT © UI8Kit
Installing primitives with the ui8kit CLI
This repo is the installer. Exact flags below are taken from src/index.ts (verified with node dist/index.js <cmd> --help).
Quick start per runtime
Always call the CLI with a pinned major: npx ui8kit@3. add all and add --all are equivalent.
# JS runtimes (writes package.json peers clsx + tailwind-merge for utils)
npx ui8kit@3 init --yes --framework react --dir src && npx ui8kit@3 add button
npx ui8kit@3 init --yes --framework vue --dir src && npx ui8kit@3 add button
npx ui8kit@3 init --yes --framework svelte --dir src && npx ui8kit@3 add button
npx ui8kit@3 init --yes --framework solid --dir src && npx ui8kit@3 add button
npx ui8kit@3 init --yes --framework webcomponent --dir src && npx ui8kit@3 add button
npx ui8kit@3 add --all # everything in r/<runtime>/index.json
# Server runtimes (no package.json is written or required)
npx ui8kit@3 init --yes --framework templ --dir internal && npx ui8kit@3 add button
npx ui8kit@3 init --yes --framework twig --dir templates && npx ui8kit@3 add button
npx ui8kit@3 init --yes --framework latte --dir templates && npx ui8kit@3 add button
npx ui8kit@3 init --yes --framework jinja2 --dir templates && npx ui8kit@3 add button
Post-install for server runtimes (the CLI prints the exact commands from item meta; it never runs them and never edits composer.json / go.mod):
- templ:
go get github.com/a-h/templ@<printed version>→templ generate(after everyadd, and in CI;*_templ.gois generated, not shipped) →go build ./.... Withgo.modpresent the utils import is rewritten to<module>/<dir>/utils. - twig / latte:
composer require "twig/twig:^3.0"(or the printed latte package), add the printed PSR-4 entry tocomposer.json, thencomposer dump-autoload:
({ "autoload": { "psr-4": { "UI8Kit\\Twig\\": "templates/runtime/" } } }UI8Kit\\Latte\\for latte). Twig:$twig->addExtension(new \UI8Kit\Twig\TwigExtension()). - jinja2:
pip install "Jinja2>=3.1,<4";from ui8kit_jinja import environment→environment().get_template("ui/button/button.html.j2").render(p={...}, children=...).
Commands and flags (ui8kit 3.2)
Global: -c, --cwd <dir>, -v, --verbose, --no-cache, -V, --version.
| Command | Flags |
|---|---|
init |
-y, --yes, --framework <react|vue|svelte|solid|webcomponent|templ|latte|twig|jinja2|none>, --runtime <name> (defaults to framework), --dir <path>, --skip-core, -r, --registry <type> (default ui), --registry-url <url>, --registry-version <v>, --strict-cdn, --import-style <alias|package> (package deprecated) |
add [components...] |
-a, --all (or add all), -f, --force, --runtime <name>, --path <key=dir> (repeatable, e.g. ui=kit), --dry-run, --retry, -r, --registry <type>, --registry-url, --registry-version, --strict-cdn |
check |
offline verify vs ui8kit.lock.json; --json, --adopt (fetches registry, adopts only matching files), --runtime, --registry-url, --registry-version, --strict-cdn (the last three only with --adopt) |
list |
--json, -r, --registry, --registry-url, --registry-version, --strict-cdn |
diff [component] |
--json, -r, --registry, --registry-url, --registry-version, --strict-cdn |
info |
--json, --cdn (resolved CDN order and overrides) |
scan |
-r, --registry <type|path>, -o, --output <file>, -s, --source <dir> |
build [registry] |
default ./src/registry.json; -o, --output <path> (default ./packages/registry/r) |
cache clear |
— |
registry clean |
--dry-run, --all, -f, --force |
reset |
--dry-run, --with-cache, -y, --yes, -f, --force |
Where registries live
- Default:
@ui8kit/registry@<CLI major>from npm via CDN, tried in order unpkg → jsDelivr:https://unpkg.com/@ui8kit/registry@3/r/<runtime>/index.jsonhttps://cdn.jsdelivr.net/npm/@ui8kit/registry@3/r/<runtime>/index.json
- Per runtime (
react,vue,svelte,solid,webcomponent,templ,twig,latte,jinja2) underr/<runtime>/:index.json— cataloguecomponents/ui/<name>.json— primitives (e.g.button.json)utils/utils.json—registry:utils(installed byinit)rules/rules.json—registry:rules→.cursor/rules/ui8kit-primitives.mdc(installed byinit)
$schema:https://cdn.jsdelivr.net/npm/@ui8kit/registry@3/schema/{schema,registry,registry-item}.json(config / index / item).- Version:
--registry-versionor"registryVersion"inui8kit.config.jsonaccepts exact (3.1.0), range (3.1) or dist-tag (canary); default is the CLI major (3→ newest 3.x.y).
Version with a custom --registry-url
--registry-version / registryVersion rewrites an npm-style name@<version> segment of a custom URL (…/@ui8kit/registry@latest/r/react, …/my-kit@2/r, …@3.1.0/…). A URL without such a segment (brand domain, self-hosted path like https://kit.example.com/v3/r/react) is used unchanged and the CLI warns that --registry-version is ignored: version those registries through their own path.
slot is React-only
slot (Slot / asChild) exists only in r/react; React bricks pull it in via registryDependencies. Other runtimes compose with their native mechanism (slots/children), so add slot --runtime vue (or any non-React runtime) prints a clear "React-only" error and skips it.
- Resolution order:
--registry-url/registryUrl→cdnBaseUrls(config) → built-in unpkg / jsDelivr. With--strict-cdn(orstrictCdn) and an explicit URL, only that URL is used. - Brand registries:
@ui8kit/registryships primitives only. Brand kits (blocks, composites) host their own registry, e.g.--registry-url https://registry.buildy.tw/react, and reference primitives by absolute URL inregistryDependencies(pinned digest, recorded inmeta.primitives). Brand items get their own lock entries. - Lock:
init/addwriteui8kit.lock.json(source URL, exact version, sha256 per file). Commit it; verify withnpx ui8kit@3 check(offline, exit 1 onmissing/modified/mixed-versions/rules-version). No lock yet →npx ui8kit@3 check --adoptonce.
Where source lives
codegen specs/ + bricks/ ─gen─▶ generated/<rt>/ (build output, NOT committed)
registry pack:runtimes ──────────▶ r/<rt>/*.json (published: @ui8kit/registry)
npm ──▶ unpkg / jsDelivr ──▶ cli: ui8kit init / add ──▶ app {dir}/ui, {dir}/utils + ui8kit.lock.json
Fix a primitive in codegen, repack in registry, publish, then add <name> --force in the app. Never fix it in the app or in r/ by hand.
Related Packages
Add Ravox Kit components, blocks and themes to your project (React, Vue, Svelte...
Install a tesserai design system into your project as components you own, keep i...
Embeddable math editor with a framework-independent model, React UI, and native...
Official CLI for Esvigy — add customizable, animated illustrations directly to y...
Version History
| Version | Released | License |
|---|---|---|
| 3.2.1 | MIT | |
| 3.2.0 | MIT | |
| 3.1.1 | MIT | |
| 3.0.1 | MIT | |
| 3.0.0 | MIT | |
| 2.0.4 | MIT | |
| 2.0.3 | MIT | |
| 2.0.2 | MIT | |
| 2.0.1 | MIT | |
| 2.0.0 | MIT | |
| 1.5.2 | MIT | |
| 1.4.2 | MIT | |
| 1.4.1 | MIT | |
| 1.3.8 | MIT | |
| 1.3.5 | MIT | |
| 1.3.0 | MIT | |
| 1.1.0 | MIT | |
| 1.0.6 | MIT | |
| 1.0.5 | MIT | |
| 1.0.4 | MIT | |
| 1.0.3 | MIT | |
| 1.0.2 | MIT | |
| 1.0.1 | MIT | |
| 0.0.8 | MIT | |
| 0.0.7 | MIT | |
| 0.0.6 | MIT | |
| 0.0.5 | MIT | |
| 0.0.4 | MIT | |
| 0.0.3 | MIT | |
| 0.0.2 | MIT |
Showing the latest 30 of 31. See every release on npm