BNA UI and the CLI that copies components into your project
BNA UI - Expo, React Native components library
At a glance
- What is it?
- BNA UI is a component library for Expo React Native apps with a twist: there is no runtime package to import, because bna-ui add writes the component source into your repository. That gives you TypeScript you own and themes you can edit, and it costs you any automatic path when the library changes upstream.
- Who is it for?
- BNA UI is a good fit for a team starting a new Expo app that wants a coherent component set and a tab-and-stack navigation layout on day one, because the init scaffold and the add command remove the first day of setup work. It is a poor fit if you intend to consume the library as a dependency and take updates, since there is no package to upgrade and the registry is a remote fetch rather than a versioned release.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 55 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The library is copied into your repo, not imported from it
The most consequential thing about BNA UI is not visible from the component list. There is no dependency to add. The CLI writes component source files into your project.
The install sequence makes that explicit:
npx bna-ui init my-app
cd my-app
npx bna-ui add button
npx bna-ui add card inputAfter you run add button, your project contains a button component file, its hooks, its theme file and the npm dependencies it needs. The README says as much when it describes the theme file: edit it and every component follows, because it is your code now.
That is the whole trade. You get a component you can read, edit and debug, with no indirection and no version negotiation, and you can never receive an upstream fix without copying it over by hand. If your team prefers owning its source, that is a feature. If your team prefers updating dependencies, there is nothing to update.
The add command accepts several names at once, as the card input example shows, and running bna-ui add with no names browses the whole set interactively.
Nine commands covering scaffolding, components and an MCP server
The command surface is broader than a component copier. The README lists them in a table, and the split between scaffolding and adding is the important part.
bna-ui init scaffolds a new Expo app with routing, theming and a tab layout. There are three sibling commands that do the same thing plus a backend: bna-ui convex adds a Convex backend with auth and OTP email flows, bna-ui supabase adds a Supabase backend with auth, RLS, realtime and storage, and bna-ui firebase adds a Firebase backend with auth, Firestore, storage and rules. That is a large amount of opinionated setup for a single command, and it is the part most worth examining before you run it.
On the component side, bna-ui add takes component names, bna-ui list prints every component, chart, hook and theme file, bna-ui search finds something by name or description, and bna-ui info prints a component's props, source and examples.
Then there is bna-ui mcp, which runs an MCP server so AI assistants can browse the registry. That is a genuinely different integration surface from the others, and it is packaged separately as @bna-ui/mcp, released at version 1.0.0 alongside bna-ui 3.0.0 on 2026-08-04.
Flags matter here too. The useful ones for add are --overwrite, --dry-run, --yes, the package manager selectors --npm, --yarn, --pnpm and --bun, and --registry <url>. Every command documents its own flags under bna-ui <command> --help.
The registry is a remote endpoint with a local cache
Components are fetched from https://ui.ahmedbna.com/r and cached under ~/.cache/bna-ui, so repeat installs work offline. The consequences are worth being clear about.
First, the install is a network fetch at the moment you run it, not a resolution of a pinned package version. The --registry <url> flag exists for exactly this reason, and the development instructions show how to point the CLI at a local registry:
pnpm --filter docs dev
cd /tmp && node ~/bna-ui/packages/cli/bin/bna-ui.js init app && cd app
node ~/bna-ui/packages/cli/bin/bna-ui.js add button --registry http://localhost:3000/rThat is the documented workflow for testing the CLI against unreleased components, and it doubles as the answer to how you would pin your own component versions: run your own registry.
Second, a cached install means a second developer on the same machine, or a CI runner, may get a different result than you did if the registry content changed in between. The cache reduces network dependence but not this ambiguity.
Third, the /r path is the registry format itself. The repository table says the docs site serves the registry at /r/*.json, so the JSON files that the CLI consumes are the same ones the documentation site renders.
One source of truth across playground, docs and installs
The monorepo layout explains why the copy-in model works at all. The repository is a pnpm and Turborepo monorepo with five meaningful paths.
packages/registry is @bna-ui/registry, the source of truth for every component, hook, theme and demo. packages/cli is the published bna-ui CLI. packages/starters holds the init, convex, supabase and firebase scaffolds. apps/docs is the documentation site, which also serves the registry at /r/*.json. apps/playground is the Expo app used for developing components.
The README states the invariant plainly: there is exactly one copy of every component, and packages/registry/src/components/ui/button.tsx is what the playground renders, what the docs site displays, and what bna-ui add button installs. So the file you receive is the same file the author saw, not a build artifact of it.
That has a direct consequence for the repository itself. Editing packages/registry/src/components/ui/<name>.tsx makes the playground hot reload, and because the same file is served to the registry, the change is what the next user installs. There is no publish step between editing a component and shipping it, which is fast to work with and also means a mistake in the registry is a mistake in every project that installs it.
Theming through useColor and a file you own
The theming model is deliberately thin. Components read colours through the useColor hook, so light and dark work without any per-component wiring:
import { useColor } from '@/hooks/useColor';
const background = useColor('background');
const primary = useColor('primary');The tokens live in theme/colors.ts, which lands in your project when you add your first component, and the file is plain data with a light and a dark block:
// theme/colors.ts
export const Colors = {
light: {
background: '#FFFFFF',
foreground: '#000000',
card: '#F2F2F7',
primary: '#007AFF',
// ... more tokens
},
dark: {
background: '#000000',
foreground: '#FFFFFF',
card: '#1C1C1E',
// ... more tokens
},
};Two things follow. The dark values are not derived from the light ones, so a brand colour has to be set twice, once per block, and forgetting the dark block is the most likely bug in a themed screen. And because the file is yours, a design change is a diff in your repository rather than a configuration change, which is exactly what you want in a design system and exactly what you do not want if you were expecting to pull updates.
The component usage itself is unremarkable React Native, with components imported from a path alias such as '@/components/ui/button', and props passed inline.
Node 22.12 for the CLI, Node 20 and pnpm 10 for the repo
There are two different Node requirements, and they belong to different audiences.
The published CLI needs Node.js 22.12 or newer. That is what you need on your machine, or in CI, to run npx bna-ui. The root package.json agrees, with an engines field of node >=20.19.4 for the workspace itself, and the development section states Node 20+ and pnpm 10+ for working on the repository. The package manager is pinned to [email protected].
The development workflow is a Turborepo script set: build runs every package in dependency order, dev starts the docs and playground together, and test, lint, typecheck and clean wrap the same task graph. To work on a component you run pnpm --filter playground ios, or android or web, and the playground hot reloads when you edit the registry source.
Platform support is listed for iOS, Android, Web, Expo Go and EAS Build. Expo Router navigation setup with tab and stack navigation is part of what init sets up, which is the Expo-specific reason to use this rather than a bare component list.
The releases tell a story about the project's shape. bna-ui 3.0.0 and @bna-ui/mcp 1.0.0 both landed on 2026-08-04, and there is a MIGRATION.md at the root, which suggests the 3.x line changed how components are delivered. The README does not describe what changed, so for an existing user that file is the place to look.
Editorial conclusion
BNA UI is a good fit for a team starting a new Expo app that wants a coherent component set and a tab-and-stack navigation layout on day one, because the init scaffold and the add command remove the first day of setup work. It is a poor fit if you intend to consume the library as a dependency and take updates, since there is no package to upgrade and the registry is a remote fetch rather than a versioned release. Before adopting, confirm the registry endpoint at ui.ahmedbna.com is reachable from your build environment and that your Node version is 22.12 or newer, decide whether the source-copy model fits your review process, and look at the theme/colors.ts file the CLI drops in, because editing it is how you customise everything downstream.
Frequently asked questions
How do I install BNA UI components in an Expo project?
Run npx bna-ui add button, or npx bna-ui add with no arguments to browse interactively, or npx bna-ui add card input to add several at once. The component source, its hooks, its theme file and its npm dependencies are written into your project. For a new app, npx bna-ui init my-app scaffolds the project first.
What Node version does the BNA UI CLI need?
The CLI requires Node.js 22.12 or newer. Working on the repository itself needs Node 20 or newer and pnpm 10 or newer, and the workspace pins [email protected] with an engines field of node >=20.19.4.
How do I customise the colours in BNA UI?
Components read colours through the useColor hook, and the tokens live in theme/colors.ts, which the CLI writes into your project when you add the first component. Edit that file and every component follows, because the theme is your own code.
Where does the BNA UI CLI fetch components from?
Components are fetched from https://ui.ahmedbna.com/r and cached under ~/.cache/bna-ui so repeat installs work offline. The --registry flag points the CLI at a different registry, which is how the development workflow tests unreleased components against a local one.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ahmedbna-ui)Was this useful?