Skip to content
Primitiv home
Framework
Consumption mode

BUILD

The registry and CLI

Styled components are not installed. They are copied into your project as files you own, edit and commit. The registry is the catalogue they are copied from, and the CLI does the copying.

This is a deliberate trade. A package is easier to update and harder to change. A copied file is the reverse: nothing upstream will overwrite it, and nothing upstream will fix it for you either. For the styling layer that is the right way round, because styling is the part you most want to change and least want changed under you.

Behaviour goes the other way. That still comes from @primitiv-ui/react as a normal dependency, so you get fixes to keyboard handling and accessibility without merging anything. You own the appearance. You do not have to own the hard part.

Your repository

  • button.tsx
  • button.recipe.ts
  • styles/primitiv/button.css
  • primitiv.json
  • primitiv.lock

Committed. Yours to edit.

behaviour, keyboard, ARIA

Installed from npm

  • @primitiv-ui/react

Updates normally.

The styling is a copy. The behaviour is a dependency.

The commands

Six commands. Most days you only use one.

Every command lists its own options with --help. On its own, it shows all six.

$ npx primitiv --help$ npx primitiv add --help

primitiv add

Copies one or more components into your project.

$ npx primitiv add button$ npx primitiv add button select modal

Useful flags:

--force
overwrites files you have changed, without asking.
--styles-only
copies the stylesheet and skips the React file.
--no-wiring
skips the project wiring, if you prefer to do it yourself.
--dry-run
shows what would happen and writes nothing.

On its first run in a project it also does the setup: installs what it needs, writes primitiv.json, generates your token layer, and wires the stylesheet import. That is intentional, so a new project is one command rather than four.

primitiv init

Sets a project up without adding a component. It asks a few questions, or takes --yes to accept the defaults, and finishes by generating the token layer.

$ npx primitiv init$ npx primitiv init --yes

primitiv tokens

Regenerates your token layer in the format your config asks for.

$ npx primitiv tokens$ npx primitiv tokens --format scss

primitiv theme

Generates a full light and dark palette from one brand colour, with every semantic role assigned by contrast. It writes into its own layer, so it beats the base tokens without editing them.

$ npx primitiv theme --brand "#0a7755"$ npx primitiv theme --brand "#0a7755" --steps 16

The status ramps take the same treatment: --danger, --warning, --success and --info. Ramps are ten steps unless you ask for another length, from 3 to 32. What that costs you is on the Tokens and theming page.

primitiv dtcg

Writes your ramps out as a design-token file, for when you want them somewhere other than a stylesheet.

$ npx primitiv dtcg --brand "#0a7755" --out palette.json

The format is standard DTCG in hex, which is what design tools import. Use it to get a palette you generated in code into Figma.

primitiv list

Shows what is installable, and what you already have.

$ npx primitiv list$ npx primitiv list --json

The two files

The CLI keeps two files in your project. Both are meant to be committed.

primitiv.json — your settings.

{  "version": 1,  "framework": "react",  "styles": { "enabled": true, "format": "css",              "path": "src/styles/primitiv" },  "tokens": { "format": "css",              "path": "src/styles/primitiv/tokens.css" },  "theme": { "brand": "#0a7755" },  "aliases": {},  "registry": { "version": "0.1.0" }}

Edit it freely. It decides where files land, which format your tokens emit in, and which registry version you are pulling from.

primitiv.lock — what you have.

A list of the components you have added and a hash of every file. The CLI uses it to tell whether you have edited a copied file, so it can ask before overwriting your work rather than assuming.

Where components come from

By default the CLI carries the registry inside itself, so add works with no network call and no version drift between the tool and the catalogue.

You can point it somewhere else. --registry takes a local path, for a private registry of your own components, or a URL, to pull a specific published version.

$ npx primitiv add button --registry ./my-registry$ npx primitiv add button --registry 0.1.0

One consequence worth knowing: because the registry is baked into the binary, a change to a component reaches you when a new CLI version is published, not before. If a component looks out of date, update the CLI.

Why a registry rather than a package

The honest answer is that it depends what you are optimising for, and this model optimises for change.

A styled component is where your product's personality lives. You will want to adjust its padding, add a variant your designer invented, or strip a prop you never use. In a package, each of those is a configuration API someone has to design, or an override that fights the library. As a file, each is an edit.

If you want the styling to update automatically and you never intend to change it, a package would serve you better, and it is fair to say so. Primitiv's answer for that case is the headless package plus your own stylesheet, which updates normally and never surprises you.