BNA UI - Expo, React Native components library
Build Native Apps - A powerful CLI for creating Expo React Native applications with a beautiful UI component library.
# Scaffold a new Expo app
npx bna-ui init my-app
cd my-app
# Add components — dependencies come along automatically
npx bna-ui add button
npx bna-ui add card input
# Or browse everything interactively
npx bna-ui add
Already have an Expo project? Skip init and run add inside it.
| Command | What it does |
|---|---|
bna-ui init [name] |
Scaffold a new Expo app with routing, theming and a tab layout |
bna-ui convex [name] |
Same, plus a Convex backend with auth and OTP email flows |
bna-ui supabase [name] |
Same, plus a Supabase backend with auth, RLS, realtime and storage |
bna-ui firebase [name] |
Same, plus a Firebase backend with auth, Firestore, storage and rules |
bna-ui add [...names] |
Add components, with their hooks, theme files and npm deps |
bna-ui list |
List every component, chart, hook and theme file |
bna-ui search |
Find something by name or description |
bna-ui info |
Print a component's props, source and examples |
bna-ui mcp |
Run an MCP server so AI assistants can browse the registry |
Requires Node.js 22.12 or newer. Run bna-ui --help for the flags
and worked examples of any command.
Useful flags for add: --overwrite, --dry-run, --yes,
--npm / --yarn / --pnpm / --bun, and --registry .
Components are fetched from https://ui.ahmedbna.com/r and cached under
~/.cache/bna-ui, so repeat installs work offline.
import React from 'react';
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { View } from '@/components/ui/view';
export default function HomeScreen() {
return (
console.log('Button pressed!')}
>
Get Started
);
}
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:
// 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
},
};
Edit that file and every component follows — it's your code now.
This is a pnpm + Turborepo monorepo.
| Path | Package | What it is |
|---|---|---|
packages/registry |
@bna-ui/registry |
Source of truth for every component, hook, theme, demo |
packages/cli |
bna-ui |
The published CLI |
packages/starters |
@bna-ui/starters |
init, convex, supabase and firebase scaffolds |
apps/docs |
— | Docs site, and the registry at /r/*.json |
apps/playground |
— | Expo app for developing components |
There is exactly one copy of every component:
packages/registry/src/components/ui/button.tsx is what the playground renders,
what the docs site displays, and what bna-ui add button installs.
Requires Node 20+ and pnpm 10+.
git clone https://github.com/ahmedbna/ui.git bna-ui
cd bna-ui
pnpm install
pnpm build # every package, in dependency order
pnpm dev # docs + playground
pnpm test
Working on a component:
pnpm --filter playground ios # or android / web
Edit packages/registry/src/components/ui/.tsx and the playground hot
reloads.
Testing the CLI against unreleased components:
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/r
We welcome contributions! See CONTRIBUTING.md for the full workflow, and CODE_OF_CONDUCT.md.
git checkout -b feat/amazing-feature)git commit -m 'feat: add amazing feature')git push origin feat/amazing-feature)Found a vulnerability? Please report it privately — see SECURITY.md.
MIT — see LICENSE.
If you find BNA UI helpful, please consider giving it a star on GitHub! It helps us a lot.
Made with ❤️ by Ahmed BNA
No open issues yet, or sync has not completed.