---
title: CLI
description: The touchcn CLI — command reference, the touchcn.json config, how updates work, using it with the shadcn CLI, and hosting your own registry.
sidebar:
  icon: terminal
---

The `touchcn` CLI distributes components as source: they are copied into your project instead of
imported from a package. It is **framework-aware** — `angular`, `react`, and `vue` are supported, and
`touchcn.json`'s `framework` field selects which registry your project reads from and where
files land.

| Command             | Purpose                                                              |
| ------------------- | -------------------------------------------------------------------- |
| [`init`](#init)     | Write a [`touchcn.json`](#touchcnjson) into your project.             |
| [`add`](#add)       | Copy components (and blocks) from the registry into your project.     |
| [`diff`](#diff)     | Show which installed components have registry updates.                |
| [`update`](#update) | Apply registry updates, three-way merging your local edits.           |
| [`build`](#hosting-your-own-registry) | Build a distributable registry from sources (registry authors). |

## Commands

### init

Writes a [`touchcn.json`](#touchcnjson) into your project and installs `@touchcn/cli` as a
devDependency, so every later command is a plain `npx touchcn …` (npx resolves the locally
installed `touchcn` bin — only this first command needs the scoped package name). Idempotent —
refuses to overwrite an existing config without `--force`. Installing the engine packages and
adding the Tailwind CSS import are printed as next steps, not applied.

```bash
npx @touchcn/cli init                    # Angular (default)
npx @touchcn/cli init --framework react
npx @touchcn/cli init --framework vue
```

| Option         | Alias | Description                                                                  |
| -------------- | ----- | ---------------------------------------------------------------------------- |
| `--framework`  |       | Target framework, `angular` (default), `react`, or `vue`. Sets the `framework` field and the per-framework default `paths.components`. |
| `--registry`   | `-r`  | Registry URL or local directory path to use instead of the official registry. |
| `--force`      | `-f`  | Overwrite an existing `touchcn.json`.                                         |
| `--no-install` |       | Skip installing `@touchcn/cli` as a devDependency.                            |

### add

Reads `touchcn.json` (walking up from the current directory), fetches each item from the
registry's framework subdirectory, resolves `registryDependencies` recursively, and copies the
files into `paths.components`.

```bash
npx touchcn add button              # a single component
npx touchcn add bottom-sheet dialog # several at once
npx touchcn add --all               # everything: every component and every block
npx touchcn add button --overwrite  # replace existing files
```

| Option         | Alias | Description                                                          |
| -------------- | ----- | -------------------------------------------------------------------- |
| `--all`        | `-a`  | Add every component and every block in the registry.                 |
| `--components` |       | Add every component in the registry.                                 |
| `--blocks`     | `-b`  | Add every block in the registry.                                     |
| `--overwrite`  | `-o`  | Overwrite existing files (otherwise they are skipped with a warning). |
| `--no-install` |       | Skip the automatic dependency installation.                          |

Each component's npm dependencies — including its `@radix-ui/*` packages on React, or `reka-ui` on
Vue — are **installed automatically** using your project's package manager (detected from the
lockfile: npm, pnpm, yarn, or bun). Pass `--no-install` to skip installation and get the install
command printed instead.

`add` also records what it installed in `touchcn.json` — the provenance that powers
[updates](#how-updates-work).

#### Blocks

[Blocks](/docs/blocks) are pre-composed, page-level screens. `add` handles them exactly like
components — by name — the only difference is where the files land: block files are copied to
`src/app/components/blocks/<name>/` (Angular) or `src/components/blocks/<name>/` (React and Vue), and each
block's component dependencies are resolved and copied into `paths.components` just like any other
`registryDependencies`.

Blocks import the components they compose through the `@/components/ui/<name>` alias, so your
project must resolve `@` to your source root — see the [Blocks overview](/docs/blocks) for the
one-time alias setup.

### diff

Reports which installed components have a newer version in the registry, and how your local files
compare to it — see [How updates work](#how-updates-work) for the file statuses it prints.

```bash
npx touchcn diff                    # every installed component
npx touchcn diff button             # a single component
npx touchcn diff --verbose          # also print a unified diff per changed file
```

| Option      | Alias | Description                                                                    |
| ----------- | ----- | ------------------------------------------------------------------------------ |
| `--verbose` | `-v`  | Append a unified diff (installed version → current version) per changed file, so you can review upstream changes before applying them. |

### update

Applies registry updates to your installed components, preserving your local edits through a
three-way merge — see [How updates work](#how-updates-work) for the merge behavior and conflict
resolution.

```bash
npx touchcn update                  # every component with an update
npx touchcn update button           # a single component (and its updated deps)
npx touchcn update --dry-run        # show what would change, write nothing
```

| Option      | Alias | Description                                                        |
| ----------- | ----- | ------------------------------------------------------------------ |
| `--dry-run` |       | Report every action without touching files or `touchcn.json`.      |

## touchcn.json

The config `init` writes and every other command reads. `add`, `diff`, and `update` find it by
walking up from the current directory.

```jsonc touchcn.json
{
  "$schema": "https://touchcn.dev/schema/touchcn.json",
  "registry": "https://raw.githubusercontent.com/capawesome-team/touchcn/main/registry/dist",
  "framework": "react",
  "paths": { "components": "src/components/ui" }
}
```

:::note
touchcn deliberately does **not** use `components.json` — the shadcn CLI owns that name with a
strict schema. Keeping touchcn's config in `touchcn.json` lets both CLIs coexist in the same
project without either tool clobbering the other.
:::

| Field              | Description                                                                 |
| ------------------ | --------------------------------------------------------------------------- |
| `registry`         | Where components are fetched from — a URL or a local directory path (useful for monorepos and testing). |
| `framework`        | `angular`, `react`, or `vue`. Selects the registry's framework subdirectory and the copy targets. |
| `paths.components` | Project-relative directory that `add` copies components into.                |
| `installed`        | Written by `add`, not by hand: per-component provenance (installed version + a content hash per file) that `diff` and `update` read. |

`paths.components` is yours to change: set it to any project-relative directory (for example
`src/app/ui`) and every subsequent `add` copies components there. Components that were added
before the change stay where they are — move them manually if needed.

## How updates work

Because components are copied source, upstream fixes don't arrive through `npm` — `diff` and
`update` are how they reach you. You own the code; these two commands are what keeps ownership from
meaning "frozen at install time".

The mechanism is the `installed` provenance map: for every component, `add` records the registry
version it copied and a hash of each file as installed. That baseline is what lets the CLI tell
*your* edits apart from *upstream* changes.

### File statuses

Per component, `diff` prints one of:

- **`up to date`** — the installed version equals the registry version.
- **`update available <from> → <to>`** — a newer version exists; each file is then classified:
  - **`pristine`** — the local file is byte-identical to the version you installed (safe to
    overwrite).
  - **`modified`** — you edited the file locally (a three-way merge will run on `update`).
  - **`new file`** — the newer version adds a file you don't have yet.
  - **`missing`** — a tracked file was deleted locally.
- **`unknown (added before provenance tracking — re-add to enable updates)`** — the component isn't
  in the `installed` map. Re-run `add --overwrite` for it to start tracking updates.

### The three-way merge

For each file, `update` does one of:

- **`pristine` → overwrite** with the current registry content.
- **`modified` → three-way merge**: the version you installed is the common ancestor (`base`), your
  file is `local`, the registry's new file is `registry`. Non-overlapping changes merge
  automatically. Overlapping changes are written **into the file** with standard conflict markers —
  there is no `.new` sibling file; the conflict lives where you resolve it:

  ```text
  <<<<<<< local
  your local lines
  ||||||| base
  the version you installed
  =======
  the incoming registry lines
  >>>>>>> registry
  ```

  Resolve a conflict by editing the file and deleting the marker lines. `update` exits non-zero when
  any file has conflicts and lists them.

A component's updated `registryDependencies` are resolved recursively and updated too, exactly like
`add`. After updating, the recorded baseline for each file becomes the **new** registry content — so
a file left with unresolved conflict markers shows up as `modified` on the next `diff`, and the next
merge uses the version you just updated to as its base.

## Use with the shadcn CLI

The touchcn registry is also a valid [shadcn](https://ui.shadcn.com) registry, so you can pull
components with `npx shadcn add` instead of the touchcn CLI if you already have shadcn in your
project. The touchcn CLI remains the first-class path — this is a convenience for existing shadcn
setups.

:::warning
**React only.** shadcn's CLI understands React registries. The touchcn **Angular** and **Vue**
registries are consumable through the **touchcn CLI only** — `npx shadcn add` cannot install them.
:::

Register the touchcn registry under a namespace in your project's shadcn `components.json` via the
`registries` map:

```jsonc components.json
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "registries": {
    "@touchcn": "https://raw.githubusercontent.com/capawesome-team/touchcn/main/registry/dist/react/{name}.json"
  }
}
```

Then add components by their namespaced name:

```bash
npx shadcn add @touchcn/button
npx shadcn add @touchcn/datepicker   # pulls its picker dependency too
npx shadcn add @touchcn/login        # a block — writes every composed file
```

shadcn resolves each item's `registryDependencies` (emitted namespaced, e.g. `@touchcn/picker`)
through the same map, and writes every file to its explicit `target`.

:::note
**Prerequisite: publish the engine packages.** Each component depends on the `@touchcn/*` npm
packages (`@touchcn/core`, `@touchcn/react`, and any `@radix-ui/*` it uses). shadcn installs those
listed dependencies automatically, so they must be available on npm for `shadcn add` to complete.
The touchcn CLI reports them the same way but never blocks on installation.
:::

What shadcn's CLI does **not** do, and why the touchcn CLI stays first-class:

- **No framework awareness.** It only reads the React registry; there is no `--framework` switch for
  the Angular or Vue sets.
- **No `diff` / `update`.** shadcn copies files once; it has no three-way merge to reconcile your
  local edits with upstream fixes. `touchcn diff` and `touchcn update` (and the `installed`
  provenance in `touchcn.json` they rely on) are touchcn-CLI only.

## Hosting your own registry

Everything below is for **registry authors** — teams distributing their own component set through
the touchcn CLI. If you only consume the official registry, you're done.

### build

Builds a distributable registry from a directory of component sources — the same mechanism the
touchcn repository uses to publish its own registry. Each component folder holds a `registry.json`
manifest plus its sources; the build inlines file contents, derives npm dependencies from the
actual imports (warning when a manifest disagrees), and emits one JSON item per component plus an
`index.json` listing. A `blocks/` subdirectory is scanned too: each of its subfolders becomes a
`registry:block` item (with a `blocks/` file target) and is listed separately in the index, so the
bulk selectors (`--components`, `--blocks`) can tell the two apart.

```bash
touchcn build --input registry/angular --output registry/dist
touchcn build --input ./src --output dist --framework react
```

| Option        | Alias | Description                                                              |
| ------------- | ----- | ------------------------------------------------------------------------ |
| `--input`     | `-i`  | Source registry directory (required).                                    |
| `--output`    | `-o`  | Output base directory (required); a `<framework>` subdirectory is appended automatically. |
| `--framework` |       | Target framework; inferred from the input directory name when omitted.   |
| `--strict`    |       | Fail on version drift (content changed without a version bump) — use in CI. |

Serve the output directory over any static URL and point `touchcn.json`'s `registry` field at
it — every CLI command then works against your registry.

### Versioned registry history

Every source `registry.json` carries a `version` (starting at `0.1.0`). Alongside the current
`<name>.json`, `build` emits an immutable history copy `<name>@<version>.json` in the same
directory, and stamps the version onto each `index.json` entry. Rebuilding the same version
overwrites only its own history file (idempotent); bumping the version adds a new history file and
never deletes older ones. This history is what makes [three-way updates](#how-updates-work)
possible — `update` fetches the exact version you installed as the common ancestor for merging.
Commit the generated history files together with the current items.

`build` guards the one discipline this scheme requires: **changing an item's sources without
bumping its version**. If content changed but the version did not, `build` prints a loud warning
(consumers would see "up to date" while the served item — and their future merge base — silently
mutates). Pass `--strict` to fail instead and leave the existing history file untouched.
