Skip to main content

CLI

The OnlyNative CLI lets you add components directly into your project as source files — no npm package for components, full ownership of the code. Inspired by shadcn/ui.

npx onlynative init
npx onlynative add button card

Why use the CLI?

With the standard @onlynative/components package, components live in node_modules and you import them as-is. The CLI takes a different approach:

  • Full ownership — component source files live in your project, not node_modules
  • Customizable — modify styles, adjust behavior, or remove what you don't need
  • No version lock-in — you decide when to pull updates
  • Tree-shake friendly — only the components you use exist in your codebase

The theme system (@onlynative/core) stays as an npm dependency so theme updates propagate automatically.

Prerequisites

  • Node.js >= 18
  • React Native >= 0.72 or Expo SDK >= 49
  • TypeScript project with path aliases configured (e.g. @/*src/*)

Quick start

1. Initialize

Run init in your React Native or Expo project:

npx onlynative init

The CLI will:

  1. Detect your project type (Expo or bare React Native)
  2. Detect your package manager (npm, yarn, pnpm, bun)
  3. Read path aliases from your tsconfig.json
  4. Ask where to place components and utility files
  5. Install @onlynative/core
  6. Create an onlynative.json config file

2. Add components

npx onlynative add button

This copies the Button source files into your project and installs any required dependencies.

3. Use them

import { ThemeProvider } from '@onlynative/core'
import { Button } from '@/components/ui/button'

export default function App() {
return (
<ThemeProvider>
<Button variant="filled" onPress={() => {}}>
Press me
</Button>
</ThemeProvider>
)
}

Commands

init

Initialize your project for OnlyNative UI.

npx onlynative init

If onlynative.json already exists, you'll be asked whether to overwrite it.

add

Add one or more components to your project.

npx onlynative add button
npx onlynative add card chip text-field
npx onlynative add appbar

Automatic dependency resolution — if a component depends on other components, they are added automatically. For example, appbar depends on icon-button and typography, so all three are added together.

Options:

FlagDescription
--force, -fOverwrite existing component files
--dry-run, -dPreview what would be installed without writing any files

What happens when you run add:

  1. The CLI fetches the component registry and validates the requested names
  2. It resolves the full dependency graph
  3. It shows a summary of what will be installed — components, utilities, and npm packages
  4. After confirmation, it fetches the source files from the registry
  5. Import paths are rewritten to match your project's alias configuration
  6. Utility files are copied to your lib/ directory
  7. A barrel file (onlynative-utils.ts) is generated that re-exports only the utilities your installed components need
  8. Any required npm packages are installed via your package manager

list

Show all available components with their install status.

npx onlynative list

Output:

Available components (v0.1.1-alpha.1):

Name Status Description
----------------------------------------------------------------------
button installed MD3 button with 5 variants...
card - Surface container with 3...
...

doctor

Diagnose common issues in your project.

npx onlynative doctor

Checks performed:

CheckDescription
Configonlynative.json exists and is valid
Core package@onlynative/core is installed
React NativeVersion meets minimum requirement (>= 0.72)
TypeScripttsconfig.json present
Component integrityAll installed component files exist on disk
Utility barrelonlynative-utils.ts exists
Peer dependenciesreact-native-safe-area-context and @expo/vector-icons status

Configuration

onlynative init creates an onlynative.json file in your project root:

{
"$schema": "https://onlynative.dev/schema.json",
"aliases": {
"components": "@/components/ui",
"lib": "@/lib"
},
"registryUrl": "https://raw.githubusercontent.com/onlynative/ui",
"registryVersion": "main"
}
FieldDefaultDescription
aliases.components@/components/uiDirectory where component folders are created
aliases.lib@/libDirectory where utility files are placed
registryUrlGitHub raw URLBase URL for fetching source files
registryVersionmainGit ref to fetch from (branch name, tag, or commit hash)

Path aliases

The CLI uses your tsconfig.json path aliases to generate clean import paths. If your project has:

{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}

Then aliases.components of @/components/ui maps to src/components/ui/ on disk, and generated imports use @/components/ui/button instead of relative paths.

Note: If you use ~/* instead of @/*, the CLI detects that and adjusts the default aliases accordingly.

Project structure

After running npx onlynative add button appbar, your project looks like this:

src/
├── components/
│ └── ui/
│ ├── button/
│ │ ├── Button.tsx
│ │ ├── types.ts
│ │ ├── styles.ts
│ │ └── index.ts
│ ├── icon-button/ ← auto-added (appbar dependency)
│ │ ├── IconButton.tsx
│ │ ├── types.ts
│ │ ├── styles.ts
│ │ └── index.ts
│ ├── typography/ ← auto-added (appbar dependency)
│ │ ├── Typography.tsx
│ │ ├── types.ts
│ │ └── index.ts
│ └── appbar/
│ ├── AppBar.tsx
│ ├── types.ts
│ ├── styles.ts
│ └── index.ts
└── lib/
├── color.ts ← color utilities (alphaColor, blendColor)
├── elevation.ts ← shadow/elevation helpers
├── icon.ts ← icon resolver
├── rtl.ts ← RTL layout helpers
└── onlynative-utils.ts ← generated barrel (re-exports used utilities)

What gets copied

Component files — each component is a self-contained directory with the same structure as the library source: the component file, types, styles, and an index barrel.

Utility files — small helper functions that components depend on. These are copied from @onlynative/utils (which is not published to npm). Only the utilities needed by your installed components are copied.

Barrel fileonlynative-utils.ts is auto-generated and re-exports only the functions your components use:

// Auto-generated by onlynative CLI. Do not edit.
export { alphaColor, blendColor } from './color'
export { elevationStyle } from './elevation'
export { getMaterialCommunityIcons } from './icon'

Import rewriting

The CLI rewrites imports in copied component files so they work in your project:

Original (library source)Rewritten to
from '@onlynative/core'Unchanged — npm package
from '@onlynative/utils'from '@/lib/onlynative-utils'
from '../icon-button'from '@/components/ui/icon-button'
from './styles'Unchanged — same directory

Available components

ComponentDependenciesDescription
typographyText with 15 MD3 type scale variants
button5 variants (filled, elevated, outlined, text, tonal), icon support
icon-button4 variants (filled, tonal, outlined, standard), toggle support
appbaricon-button, typographyTop app bar, 4 layout variants, SafeAreaView
card3 variants (elevated, filled, outlined), optional press handler
chip4 variants (assist, filter, input, suggestion), icon/avatar support
checkboxBinary selection control
radioSingle-choice selection control
switchToggle control with optional icons
text-fieldAnimated floating label, 2 variants (filled, outlined)
layoutBox, Row, Column, Grid, and SafeAreaView wrapper
listList container with interactive items and dividers
keyboard-avoiding-wrapperZero-config keyboard-aware wrapper for forms

Comparison with npm packages

CLI (npx onlynative add)npm (@onlynative/components)
Component code lives inYour project (src/)node_modules/
CustomizationEdit source directlyOverride via props/theme only
UpdatesRe-run add --force when you choosenpm update
Bundle sizeOnly what you addTree-shaking at build time
Setupnpx onlynative initpnpm add @onlynative/components

Both approaches use @onlynative/core for theming — they are fully compatible and you can even mix them in the same project.