Getting started
CLI
Copy components into your project, then keep them updated without losing your edits.
Overview#
The CLI is @junco/cli. Run it with npx, or install it and use the junco command. It needs Tailwind CSS v4 already set up (an @import "tailwindcss"; in your global CSS).
npx @junco/cli init # detect Next/Vite, wire the theme into your CSS, write junco.json
npx @junco/cli add button select # copies select + field, label, icons, utils; installs npm deps
npx @junco/cli diff # which installed components have upstream updates
npx @junco/cli diff select # unified diff: your copy vs upstream
npx @junco/cli update select # take upstream if you didn't edit it; otherwise write a side file| Command | What it does |
|---|---|
init | Detects your framework, CSS file and @/* alias, wires the Junco theme into your CSS, adds cn() and writes junco.json. |
add <name...> | Copies components and everything they depend on, rewrites imports to your aliases, installs missing npm packages. |
diff [name...] | With names: a unified diff of your copy against upstream. Without: lists components with updates. |
update [name...] | Three-way update. Untouched files are replaced; edited ones get a .junco-upstream copy to merge by hand. |
list | Everything in the registry, marked installed or update available. |
eject | Not built yet. It will turn an installed (npm) component into a local copy. |
init#
npx @junco/cli init
npx @junco/cli init --css inline --yes| Flag | Description |
|---|---|
--css package | Default. Adds @import "@junco/tailwind"; to your CSS and installs the package. |
--css inline | Writes the whole theme to styles/junco.css in your project instead, so you own it. |
--css-file <path> | Which stylesheet to edit, when auto-detection picks the wrong one. |
--yes, -y | Accept the defaults without asking. |
--force, -f | Overwrite an existing junco.json. |
Projects without an @/* alias get relative imports. Safe to re-run.
add#
npx @junco/cli add button field select
npx @junco/cli add data-table form # the junco/data-table and junco/form piecesDependencies are resolved recursively (Select brings Field, Label and the icons it uses). Each file's content hash is recorded in junco.json, which is what makes diff and update work. Files you already changed are kept unless you pass --overwrite.
diff and update#
npx @junco/cli diff # what has updates
npx @junco/cli diff select > select.patch # applyable with patch
npx @junco/cli update select
npx @junco/cli update --allCopied code usually drifts the moment you edit it. update compares three versions: what Junco wrote, your file now, and the latest upstream. If you never touched a file, it's replaced. If you did, the new version lands next to it as select.tsx.junco-upstream; merge what you want, delete the copy, and run update select --resolved. --force takes upstream and drops your edits.
junco.json#
{
"$schema": "https://junco.madebytobi.com/schema/junco.json",
"registry": "https://junco.madebytobi.com/r",
"framework": "next",
"css": { "file": "app/globals.css", "mode": "package" },
"aliases": { "components": "@/components", "ui": "@/components/ui", "lib": "@/lib", "utils": "@/lib/utils" },
"paths": { "components": "components", "ui": "components/ui", "lib": "lib" },
"installed": {
"button": { "hash": "3f1c…", "files": { "components/ui/button.tsx": "9ab2…" } }
}
}| Option | Description |
|---|---|
--registry <url|dir> | Registry to read. Also JUNCO_REGISTRY or registry in junco.json. A local folder works too. |
--cwd <dir> | Run as if from another folder. |
--no-install | Write files but don't install npm packages. |
With the shadcn CLI#
Every component is also a shadcn registry item, so the shadcn CLI works too. It follows the dependency URLs and merges the theme into your CSS:
npx shadcn@latest add https://junco.madebytobi.com/r/button.json@/* alias in your root tsconfig.json, or it writes into a literal @/ folder. That's standard shadcn setup.