Getting started

CLI reference

The commands you will actually use.

Add a component

bunx --bun shadcn@latest add @woohl/button

Resolves the whole chain: npm packages, internal registry dependencies, and the token layer. Files land at the paths your components.json aliases define.

Add several at once

bunx --bun shadcn@latest add @woohl/dialog @woohl/dropdown @woohl/select

Shared dependencies are resolved once, not per item.

Where things land

Exactly where anything from ui.shadcn.com would: the CLI resolves code against the aliases in your own components.json, so these paths follow whatever you have configured. The defaults:

Item typeInstalls toExample
registry:ui<ui alias>/<file>src/components/ui/button.tsx
registry:block<components alias>/<file>src/components/price.tsx
registry:lib<lib alias>/<file>src/lib/utils.ts

The class merger arrives as lib/utils.ts exporting cn, which is the file your utils alias already points at — it does not stand up a second helper beside the one you have.

Two things are not code and so carry an explicit target:

ItemInstalls to
@woohl/tokensstyles/**
@woohl/brand-assetsassets/brand/**

Why the stylesheets are files

The token layer is around 1,400 lines of @theme and @theme inline blocks across sixteen stylesheets. That is not expressible as a registry item's cssVars map, so it ships as files and you wire it with one @import — see Installation.

Updating

There is no update command. Re-running add overwrites the file, which is the honest model for source you own: your local edits are yours, and taking a new version means deciding what to do with them.

bunx --bun shadcn@latest add @woohl/button --overwrite

Diff before you accept it if you have edited the file — the CLI will not merge for you.

Building the registry

From inside woohl-ui:

bun run registry:build   # compile registry/ into public/r/*.json
bun run build            # registry + MDX + next build
bun run start -p 4321    # serve it for @woohl-local installs

registry:build derives every dependency from the source imports, so adding an import to a component is enough — the manifest updates itself.

On this page