Skip to content

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
Commands
CommandWhat it does
initDetects 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.
listEverything in the registry, marked installed or update available.
ejectNot 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
FlagDescription
--css packageDefault. Adds @import "@junco/tailwind"; to your CSS and installs the package.
--css inlineWrites 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, -yAccept the defaults without asking.
--force, -fOverwrite 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 pieces

Dependencies 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 --all

Copied 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…" } }
  }
}
Global options
OptionDescription
--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-installWrite 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
On Vite, shadcn needs the @/* alias in your root tsconfig.json, or it writes into a literal @/ folder. That's standard shadcn setup.