CLI

PreviousNext

Use the BNA UI CLI to add components to your React Native and Expo projects.

init

Use the init command to initialize a new BNA project with configuration and dependencies.

The init command creates a new project, installs dependencies, adds utils, hooks, themes and sets up the component structure for your mobile project.

pnpm dlx bna-ui init [project-name]

Options

Usage: bna-ui init [options] [project-name]
 
Scaffold a new Expo app with BNA UI set up
 
Arguments:
  project-name    Name of the project, or `.` for the current directory
 
Options:
  --npm           Install with npm
  --yarn          Install with yarn
  --pnpm          Install with pnpm
  --bun           Install with bun
  --verbose       Print the full error stack on failure
  --skip-install  Create the project without installing dependencies
  -h, --help      display help for command
 
Examples
  $ bna-ui init my-app
  $ bna-ui init .                  # scaffold into the current directory
  $ bna-ui init my-app --pnpm --skip-install

convex

Use the convex command to initialize a new BNA project with Convex backend integration.

The convex command creates a new project with Convex configured, installs dependencies, and sets up the backend structure.

pnpm dlx bna-ui convex [project-name]

By default this scaffolds the full authentication setup — Google, Apple, password and email OTP sign-in. Pass --no-auth for a Convex backend with no sign-in at all: a schema, a demo query, and nothing to delete before you start.

pnpm dlx bna-ui convex [project-name] --no-auth

See Expo + Convex and Expo + Convex + Auth for what each one gives you.

Options

Usage: bna-ui convex [options] [project-name]
 
Scaffold a new Expo app with a Convex backend
 
Arguments:
  project-name    Name of the project
 
Options:
  --npm           Install with npm
  --yarn          Install with yarn
  --pnpm          Install with pnpm
  --bun           Install with bun
  --verbose       Print the full error stack on failure
  --skip-install  Create the project without installing dependencies
  --skip-convex   Skip provisioning the Convex backend
  --no-auth       Scaffold Convex without authentication
  -h, --help      display help for command
 
Examples
  $ bna-ui convex my-app
  $ bna-ui convex my-app --no-auth
  $ bna-ui convex my-app --skip-convex   # wire it up yourself later

supabase

Use the supabase command to initialize a new BNA project with a Supabase backend — Postgres, row level security, realtime, storage and edge functions.

pnpm dlx bna-ui supabase [project-name]

By default this scaffolds the full authentication setup — password, magic links, email OTP, Google, Apple and GitHub. Pass --no-auth for a backend with no sign-in: migrations, a live query, storage and an edge function.

pnpm dlx bna-ui supabase [project-name] --no-auth

After copying files it asks for your project URL and publishable key, writes them to .env.local, and then — only if the Supabase CLI is on your PATH — links the project, applies the migrations and generates lib/database.types.ts. If it is not installed, those commands are printed as next steps instead. Nothing fails either way.

See Expo + Supabase and Expo + Supabase + Auth for what each one gives you.

Options

Usage: bna-ui supabase [options] [project-name]
 
Scaffold a new Expo app with a Supabase backend
 
Arguments:
  project-name     Name of the project
 
Options:
  --npm            Install with npm
  --yarn           Install with yarn
  --pnpm           Install with pnpm
  --bun            Install with bun
  --verbose        Print the full error stack on failure
  --skip-install   Create the project without installing dependencies
  --skip-supabase  Skip linking the project and applying migrations
  --no-auth        Scaffold Supabase without authentication
  -h, --help       display help for command
 
Examples
  $ bna-ui supabase my-app
  $ bna-ui supabase my-app --no-auth
  $ bna-ui supabase my-app --skip-supabase

firebase

Use the firebase command to initialize a new BNA project with a Firebase backend — Cloud Firestore, Cloud Storage, and security rules with tests that execute them.

pnpm dlx bna-ui firebase [project-name]

By default this scaffolds the full authentication setup — email and password, Google and Apple, onboarding and a profile. Pass --no-auth for a backend with no sign-in: a live task list, storage uploads with progress, and open rules.

pnpm dlx bna-ui firebase [project-name] --no-auth

After copying files it asks for your Firebase web config — deriving the auth domain and storage bucket from the project ID as defaults — writes .env.local, sets the default project in .firebaserc, and then, only if firebase-tools is on your PATH, selects the project and deploys the rules and indexes. If it is not installed, those commands are printed as next steps instead. Nothing fails either way.

See Expo + Firebase and Expo + Firebase + Auth for what each one gives you.

Options

Usage: bna-ui firebase [options] [project-name]
 
Scaffold a new Expo app with a Firebase backend
 
Arguments:
  project-name     Name of the project
 
Options:
  --npm            Install with npm
  --yarn           Install with yarn
  --pnpm           Install with pnpm
  --bun            Install with bun
  --verbose        Print the full error stack on failure
  --skip-install   Create the project without installing dependencies
  --skip-firebase  Skip collecting the config and deploying the rules
  --no-auth        Scaffold Firebase without authentication
  -h, --help       display help for command
 
Examples
  $ bna-ui firebase my-app
  $ bna-ui firebase my-app --no-auth
  $ bna-ui firebase my-app --skip-firebase

add

Use the add command to add components and dependencies to your React Native or Expo project.

pnpm dlx bna-ui add [component]

Options

Usage: bna-ui add [options] [components...]
 
Add components to an existing project
 
Arguments:
  components        Component names. Omit to pick interactively.
 
Options:
  --npm             Install with npm
  --yarn            Install with yarn
  --pnpm            Install with pnpm
  --bun             Install with bun
  --verbose         Print the full error stack on failure
  --overwrite       Replace files that already exist
  --dry-run         Show what would be written, and write nothing
  -y, --yes         Skip prompts, keeping any existing files
  --registry <url>  Registry to fetch from
  -h, --help        display help for command
 
Examples
  $ bna-ui add button
  $ bna-ui add button input card
  $ bna-ui add                     # browse and pick
  $ bna-ui add button --dry-run
  $ bna-ui add button --registry http://localhost:3000/r

Where components come from

add fetches component source from https://ui.ahmedbna.com/r and writes it into your project. Responses are cached under ~/.cache/bna-ui and revalidated with ETags, so repeat installs are fast and work offline.

Point it somewhere else with the --registry flag or the BNA_UI_REGISTRY environment variable:

pnpm dlx bna-ui add button --registry http://localhost:3000/r

Adding a component brings its whole dependency chain — button also installs the text, icon and spinner it composes, the hooks and theme files they import, and any npm packages they need.

list

Every component, chart, hook and theme file the registry ships.

pnpm dlx bna-ui list
npx bna-ui list --type chart

Options

Usage: bna-ui list|ls [options]
 
List every component, chart, hook and theme file
 
Options:
  --type <type>     Narrow to ui, chart, hook or theme
  --json            Emit as JSON, for scripts and agents
  --registry <url>  Registry to read from
  --verbose         Print the full error stack on failure
  -h, --help        display help for command
 
Examples
  $ bna-ui list
  $ bna-ui list --type chart
  $ bna-ui list --json

Find something without knowing its exact name.

pnpm dlx bna-ui search chart

Options

Usage: bna-ui search [options] <query>
 
Search the registry by name or description
 
Arguments:
  query             Search term, e.g. "chart" or "date"
 
Options:
  --json            Emit as JSON, for scripts and agents
  --registry <url>  Registry to read from
  --verbose         Print the full error stack on failure
  -h, --help        display help for command
 
Examples
  $ bna-ui search chart
  $ bna-ui search "date picker"

info

A component's description, props, install command, source and examples — the same bundle the MCP server serves. --json makes it machine-readable.

pnpm dlx bna-ui info button
npx bna-ui info button --json

Options

Usage: bna-ui info [options] <component>
 
Print a component's props, source and examples
 
Arguments:
  component         Component name, e.g. button
 
Options:
  --json            Emit the full bundle as JSON, for scripts and agents
  --registry <url>  Registry to fetch from
  --verbose         Print the full error stack on failure
  -h, --help        display help for command
 
Examples
  $ bna-ui info button
  $ bna-ui info button --json | jq .meta

mcp

Runs an MCP server over stdio so an AI assistant can browse and read the registry itself. See MCP for editor configuration.

claude mcp add bna-ui -- npx -y bna-ui mcp

The server ships as a separate package, @bna-ui/mcp, and bna-ui mcp hands off to it. That keeps the MCP SDK — which pulls in HTTP and SSE transport dependencies this server never uses — out of every npx bna-ui add.

components.json

init writes one. It is optional: every command works without it, on the same defaults. It exists so you stop repeating yourself.

components.json
{
  "registry": "https://ui.ahmedbna.com/r",
  "aliases": {
    "components": "ui-kit"
  },
  "packageManager": "pnpm"
}
KeyEffect
registryWhere add, list, search and info fetch from
aliasesWhere each kind of file is written, relative to the @/ root
baseDirOverrides where @/ points, when the tsconfig reading is wrong
packageManagerWhich manager installs npm dependencies

Where files land

add writes relative to whatever your tsconfig.json maps @/* onto — the same alias every installed file imports through. Nothing to configure:

"@/*" in your tsconfigadd button writes
["./*"]components/ui/button.tsx
["./src/*"]src/components/ui/button.tsx

baseUrl is honoured, and jsconfig.json works the same way. Set baseDir to override the detection — useful when the alias is declared in a base config you extends rather than in the project's own tsconfig.

Aliases

aliases moves a kind of file to a different directory inside @/, and add rewrites the matching import specifiers in the source it copies so everything still resolves. With the config above, add button writes ui-kit/ui/button.tsx and its imports read @/ui-kit/ui/text. Only the leading segment is remapped — the ui/ beneath it is part of the registry's own layout.

Note these are relative to the @/ root, not to the project root. In an app whose @/* maps to ./src/*, components already land under src/ — writing "components": "src/components" would nest them twice, so add ignores the redundant prefix and tells you to drop it.

Examples

Initialize a new project

pnpm dlx bna-ui init my-app

Initialize with Convex backend

pnpm dlx bna-ui convex my-convex-app

Initialize with Supabase backend

pnpm dlx bna-ui supabase my-supabase-app

Initialize with Firebase backend

pnpm dlx bna-ui firebase my-firebase-app

Initialize using specific package manager

pnpm dlx bna-ui init my-app --pnpm

Skip package installation

pnpm dlx bna-ui init my-app --skip-install

Add a button component

pnpm dlx bna-ui add button

Add multiple components

pnpm dlx bna-ui add button input card

Add component with overwrite

pnpm dlx bna-ui add button --overwrite

Preview what would be installed

pnpm dlx bna-ui add button --dry-run

Add components without confirmation

pnpm dlx bna-ui add button input --yes

Package managers

Pass a flag to force one:

  • --npm
  • --yarn
  • --pnpm
  • --bun

Without a flag the CLI works it out, in this order:

  1. packageManager in your components.json.
  2. How you invoked it — npx, pnpm dlx, bunx.
  3. Your project's lockfile or its packageManager field.
  4. npm.

The scaffold commands print which one they picked.

Why scaffolds pin a linker

Metro and React Native's autolinking both read node_modules off disk, so Expo needs a real, flat one. Two package managers do not give it one by default, and scaffolds ship the config that fixes each.

yarn

Yarn 2+ installs Plug'n'Play by default — a .pnp.cjs resolver and no node_modules directory at all, which Metro cannot read. Scaffolds ship:

.yarnrc.yml
nodeLinker: node-modules

Yarn 1.x is flat already and ignores this file.

pnpm

Scaffolds ship an .npmrc and a pnpm-workspace.yaml that both pin pnpm to the hoisted linker:

.npmrc
node-linker=hoisted
pnpm-workspace.yaml
nodeLinker: hoisted

Expo needs a flat node_modules — Metro resolves its own empty module out of metro-config's require paths, and React Native's autolinking walks node_modules by hand. Under pnpm's default isolated layout expo start fails with Unable to resolve module …/metro-runtime/src/modules/empty-module.js. Both files ship because pnpm 11 reads settings only from pnpm-workspace.yaml while pnpm 10.15 and older read only .npmrc.

If you have a project scaffolded before this landed, add both files and reinstall. Clear Metro's cache on the first run — switching an existing project from the isolated layout to the hoisted one turns node_modules/react-native from a symlink into a real directory, and a warm cache fails on the change with TreeFS: Could not add directory node_modules/react-native:

rm -rf node_modules pnpm-lock.yaml
pnpm install
npx expo start --clear