lattice-uiStable directionimport latticeThe lattice CLI is a Node tool for Lattice UI projects. It scaffolds new rbxts/react games, installs primitive packages together with their required peers and providers, and audits an existing project for missing dependencies. It is published to npm as lattice-ui and exposes two equivalent binaries: lattice and lattice-ui.
Use the CLI when you want package selection, peer resolution, and project health checks handled for you instead of editing package.json by hand.
Running the CLI
The CLI requires Node 20+. Run it on demand with npx — no install needed:
npx lattice-ui <command> [options]Or install it globally to get the shorter lattice binary:
pnpm add lattice-uinpm install lattice-uiyarn add lattice-uibun add lattice-uinpm i -g lattice-uilattice <command> [options]Both binaries (lattice and lattice-ui) run the same program. Run lattice help for the built-in usage summary or lattice version to print the installed version.
npx lattice does not work — npx resolves the published package name, not the shorthand binary alias, so it fails to find anything to run. Always write npx lattice-ui. The short lattice form is for an installed CLI, global or local.
Help
Every command has its own help page as of 0.7.0, and three spellings reach it:
lattice add --helplattice help addA malformed run reaches it too — lattice add --bogus --help prints the add page rather than complaining about the unknown option first. -h works everywhere --help does.
A command’s page lists its usage, its own options, and its examples. For add, remove, and upgrade it also lists every available component and preset — previously the only way to see that list was to start an interactive run and read the prompt.
Global options
| Prop | Type | Description |
|---|---|---|
| --help, -h | flag | Print usage. As the first argument it short-circuits any command; after a command name it prints that command's page. |
| --version, -v | flag | Print the installed CLI version. |
| --verbose | flag | Stream debug logging, per-file copy output, and the package manager's own output live instead of buffering it. |
| --yes, -y | flag | Accept defaults and skip prompts. Accepted by every command that prompts. |
An unknown option is a usage error, but not a bare one: the CLI suggests the closest valid name and points at the relevant help page.
Unknown option for add: --prest Did you mean --preset? Run `lattice add --help` to see the supported options.The same suggestion machinery covers command names, component names, and preset names, so lattice add dialogg proposes dialog rather than failing blankly. Component and preset names are validated before a package manager is resolved — previously a typo could surface an unrelated package-manager error first whenever more than one manager was installed.
Terminal behavior
| Variable | Effect |
|---|---|
NO_COLOR | Disables colored output when set to any non-empty value. Wins over FORCE_COLOR. |
FORCE_COLOR | Forces color through a pipe. Any value except 0 enables it. |
NO_HYPERLINK | Disables OSC 8 hyperlinks. |
FORCE_HYPERLINK | Forces hyperlinks on. Any value except 0 enables it. |
Color previously keyed off TTY detection alone. It now honors the NO_COLOR/FORCE_COLOR contract first, then falls back to TTY detection, and TERM=dumb always disables it.
Project paths and package names are printed as OSC 8 hyperlinks so they are clickable in terminals that support them. Paths are shortened against the working directory and ~. Hyperlinks are off automatically under CI and outside a terminal, since a few terminals print the raw escape sequence rather than ignoring it.
When output is not a terminal, the CLI drops to a flat ASCII layout so logs and CI transcripts stay readable. Everything below describing the connected-run layout applies to interactive terminals.
Output
Each run prints as one connected flow: a vertical gutter threads the header, each titled block, and the closing verdict together, with tree branches for the rows inside a block and the elapsed time on the last line. This replaced the five ceremonial sections every command used to print — counts now live in the heading of the block they describe, so doctor no longer states its warning count three times, and the ? glyph is gone from informational lines where it read as a question.
Commands also report which package manager they resolved and why.
While a progress spinner is running, the package manager’s own output is buffered and surfaced only if the command fails. Inherited output used to interleave with the animation, scattering spinner frames through the install log. Pass --verbose to stream it live instead.
The flag was documented by the logger but never parsed, so logger.debug and the verbose file-copy logging were unreachable before 0.7.0.
Interactive prompts
Selection prompts are keyboard-driven as of 0.7.0. They previously took a comma-separated list of numbers, and an answer that failed to parse aborted the whole command instead of re-asking.
| Key | Action |
|---|---|
↑ / ↓, or k / j | Move the cursor |
Space | Toggle the highlighted option |
a | Select all |
n | Select none |
Enter | Confirm |
Ctrl-C or Esc | Cancel |
The list scrolls when it is taller than the terminal, keeping the cursor in view. Cancelling restores the cursor and takes the terminal out of raw mode — Ctrl-C previously exited before the restore could run, leaving the cursor hidden.
Commands
| Command | Purpose |
|---|---|
create | Scaffold a new project from the rbxts template. |
init | Add Lattice to an existing project. |
add | Install component packages plus their peers and required providers. |
remove | Remove selected component packages. |
upgrade | Upgrade installed @lattice-ui/* packages. |
doctor | Audit lockfiles, peers, and provider expectations. |
help / version | Print usage or the CLI version. |
create
Scaffolds a new project into an optional [project-path] (defaults to the current directory) from the rbxts template.
npx lattice-ui create my-game --pm npm --git --no-lint| Prop | Type | Description |
|---|---|---|
| [project-path] | positional | Target directory for the new project. At most one is accepted; defaults to the current directory. |
| --yes, -y | flag | Accept defaults and skip interactive prompts. |
| --pm | <pnpm | npm | yarn> | Force a package manager instead of auto-detecting one. |
| --git | flag | Initialize a git repository in the new project. |
| --template | <rbxts> | Project template to scaffold from. Currently rbxts. |
| --lint | flag | Include the lint setup. Conflicts with --no-lint. |
| --no-lint | flag | Skip the lint setup. Conflicts with --lint. |
init
Initializes Lattice inside an existing project. Unlike create, it accepts no positional path and no --git/--no-lint flags, and it supports --dry-run.
npx lattice-ui init --dry-runinit is also the migration path off the 0.6.1 rename. Before merging the template it rewrites any pre-0.6.1 package names in dependencies/devDependencies to their react- replacements (@lattice-ui/dialog → @lattice-ui/react-dialog, @lattice-ui/core → @lattice-ui/react-runtime), resolving a version for each replacement it has to add. Dropping the old name matters: leaving both in the manifest makes the old and new copies resolve side by side, which npm then rejects on peer resolution.
If the install fails, init and create roll their file changes back rather than leaving a half-written scaffold for the next run to merge on top of.
| Prop | Type | Description |
|---|---|---|
| --yes, -y | flag | Accept defaults and skip prompts. |
| --dry-run | flag | Show what would change without writing files. |
| --pm | <pnpm | npm | yarn> | Force a package manager instead of auto-detecting one. |
| --template | <rbxts> | Template to initialize from. |
| --lint | flag | Include the lint setup. |
add
Installs one or more component packages along with everything they need. Pass component names directly (comma- or space-separated) and/or --preset groups. With no names or presets and without --yes, add prompts you to pick presets and components interactively. Run lattice add --help to see every available component and preset without starting a run.
npx lattice-ui add dialog,toast --preset overlayFor each selected component the CLI gathers its npm package, its peers (@rbxts/react, @rbxts/react-roblox), and any required providers, then installs the set minus whatever is already present. Optional providers are reported as suggestions but not installed. After a real run it points you at doctor.
context-menu and motion were missing from the registry before 0.7.0, so lattice add context-menu and lattice add motion failed with “Unknown component”. Both resolve now.
| Prop | Type | Description |
|---|---|---|
| [name...] | positional | Component names to add. Comma-separated lists are split (e.g. dialog,toast). |
| --preset | <preset...> | Add every component in a preset. Repeatable and comma-separated. |
| --pm | <pnpm | npm | yarn> | Force a package manager instead of auto-detecting one. |
| --yes, -y | flag | Skip the install confirmation prompt. |
| --dry-run | flag | Print the planned install command without changing anything. |
remove
Removes the selected component packages. Accepts the same selection inputs and flags as add.
npx lattice-ui remove dialog --dry-run| Prop | Type | Description |
|---|---|---|
| [name...] | positional | Component names to remove (comma- or space-separated). |
| --preset | <preset...> | Remove every component in a preset. |
| --pm | <pnpm | npm | yarn> | Force a package manager. |
| --yes, -y | flag | Skip the confirmation prompt. |
| --dry-run | flag | Preview the changes without applying them. |
upgrade
Upgrades installed @lattice-ui/* packages. Same selection inputs and flags as add and remove. Pre-0.6.1 package names are skipped rather than upgraded — their dist-tags no longer move, so run init to rewrite them to their react- replacements first.
npx lattice-ui upgrade --dry-run| Prop | Type | Description |
|---|---|---|
| [name...] | positional | Specific packages to upgrade. Omit to consider all installed @lattice-ui packages. |
| --preset | <preset...> | Upgrade every component in a preset. |
| --pm | <pnpm | npm | yarn> | Force a package manager. |
| --yes, -y | flag | Skip the confirmation prompt. |
| --dry-run | flag | Preview the upgrade without applying it. |
doctor
Audits the project and reports issues without changing anything, then finishes with recommended commands. It checks for:
| Check | Level |
|---|---|
| Missing or duplicated lockfile | warn |
A packageManager field that disagrees with the resolved manager | warn |
No @lattice-ui component packages installed at all | warn |
| A pre-0.6.1 package name that no longer receives releases | error |
A typescript major other than the one roblox-ts compiles with | warn |
An @lattice-ui package that is installed but not in the CLI registry | warn |
| A missing peer, or a missing optional provider | warn |
| A missing required provider | error |
Errors exit non-zero; warnings do not.
npx lattice-ui doctor| Prop | Type | Description |
|---|---|---|
| --pm | <pnpm | npm | yarn> | Force the package manager used for the audit instead of auto-detecting. |
Presets
Presets are named bundles of components so you can install a related set in one go with --preset.
| Preset | Components |
|---|---|
overlay | popover, tooltip, dialog, toast |
form | checkbox, radio-group, switch, text-field, textarea |
Package manager resolution
When --pm is omitted, the CLI resolves a package manager automatically from the project lockfile or installed managers; pass --pm <pnpm|npm|yarn> to override that choice. doctor surfaces resolution problems: a missing lockfile, multiple lockfiles, or a packageManager field that does not match the resolved manager.
The resolved manager is also pinned into the project. create and init record it as a devEngines.packageManager entry, and add, remove, and upgrade reapply the pin before they touch dependencies — if the pin names a different manager than the one this run uses, the CLI warns and repins. The pin is what keeps a project scaffolded with pnpm from being quietly installed with npm: both managers refuse to install when the pin names the other one.
Every component declares the peers it needs (@rbxts/react, @rbxts/react-roblox) and, for layered primitives, optional providers such as @lattice-ui/react-layer’s PortalProvider. add installs peers and required providers automatically but only suggests optional providers — wire those up yourself when you want the behavior they enable (for example, portal-mounted dialog or menu content).
Dry runs
init, add, remove, and upgrade all accept --dry-run. A dry run prints the exact package-manager command it would run and reports what it would do, which is the safest way to preview a change before committing to it.
Before 0.7.0 a dry run closed by claiming work it had not done — lattice add --dry-run reported “Added components: …” and “Installed packages: 4” despite changing nothing, and remove and upgrade did the same.