npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kyaw-min-khant/rnc

v2.1.0

Published

shadcn-style React Native components — copied into your project via CLI, not installed as a runtime dependency

Readme

RNC — React Native Components

shadcn-style React Native components with full StyleSheet and NativeWind (Tailwind) support. There's nothing to npm install — a CLI copies each component's source directly into your project, so you own the code and only add what you actually use. Every component is theme-aware — customize colors and roundness once via ThemeProvider and all components update automatically.


Table of Contents


Getting Started

npx @kyaw-min-khant/rnc init
npx @kyaw-min-khant/rnc add button

init asks where you want components, the theme, and utilities placed in your project, and whether to use an @/-style import alias instead of relative imports (recommended — say yes and it wires up tsconfig.json for you). It writes those choices to a components.json. add then copies the requested component's source there — along with any shared files it needs (like ThemeContext.tsx) — rewriting its imports to match your setup automatically.

The examples below assume the defaults offered by init:

| Alias | Default path | |---|---| | components | src/components/ui | | theme | src/theme | | utils | src/lib |

with the import alias enabled (@ → src). If you picked different paths, or opted out of the alias, adjust the import paths in the examples accordingly — with the alias off, the same imports look like ../../theme/ThemeContext (relative), still correctly rewritten either way.


Setup

Wrap your root component with ThemeProvider, exported from the theme file rnc add copies in. You can pass a partial theme — anything you omit falls back to the default.

import { ThemeProvider } from '@/theme/ThemeContext';

export default function App() {
  return (
    <ThemeProvider
      theme={{
        colors: {
          primary: '#6200ee',
          secondary: '#03dac6',
        },
        roundness: 8,
      }}
    >
      <YourApp />
    </ThemeProvider>
  );
}

CLI Reference

rnc init

Creates components.json in the current directory. Prompts for three paths (components, theme, utils), and whether to use an import alias:

  • Yes (recommended) — prompts for an alias prefix (default @) and the directory it should point to (default src), writes @/* → src/* into compilerOptions.paths in tsconfig.json (creating a minimal one if you don't have one), and prints the babel-plugin-module-resolver snippet to add to babel.config.js so Metro resolves it at runtime too — tsconfig.json alone only helps TypeScript/your editor, not the bundler.
  • No — components are wired together with plain relative imports (../../theme/ThemeContext), no extra config needed.

Refuses to overwrite an existing components.json without confirmation.

rnc add

npx @kyaw-min-khant/rnc add <component> [components...]
npx @kyaw-min-khant/rnc add dropdown skeleton --overwrite

Copies one or more components into your project. Resolves each component's dependencies automatically (e.g. adding dropdown also adds theme and utils if you don't already have them) and installs any npm packages a component needs via whichever package manager your project uses (detected from package-lock.json / yarn.lock / pnpm-lock.yaml / bun.lockb). Existing files are skipped unless you pass -o, --overwrite.

rnc list

Prints every available component name and a one-line description.

components.json

{
  "$schema": "https://kyaw-min-khant.dev/rnc/schema/components.json",
  "aliases": {
    "components": "src/components/ui",
    "theme": "src/theme",
    "utils": "src/lib"
  },
  "importAlias": {
    "prefix": "@",
    "baseDir": "src"
  }
}

aliases controls where files are written on disk — point these at wherever fits your project structure. importAlias is optional; omit it (or answer "No" during init) to fall back to relative imports. When present, rnc add rewrites every copied component's internal imports as <prefix>/<path relative to baseDir> (e.g. @/theme/ThemeContext) instead of a relative path — falling back to relative for any dependency that lands outside baseDir.


Theme

RNCTheme interface

| Token | Default | Description | |---|---|---| | colors.primary | #6200ee | Main brand color | | colors.secondary | #03dac6 | Secondary brand color | | colors.background | #ffffff | Screen background | | colors.surface | #f5f5f5 | Card / input background | | colors.error | #b00020 | Error states | | colors.success | #388e3c | Success states | | colors.warning | #f57c00 | Warning states | | colors.onPrimary | #ffffff | Text/icon on primary bg | | colors.onSecondary | #000000 | Text/icon on secondary bg | | colors.onSurface | #000000 | Text/icon on surface | | colors.outline | #6200ee | Borders and outlines | | roundness | 8 | Border radius for all components |

useTheme()

Access the active theme inside your own components:

import { useTheme } from '@/theme/ThemeContext';

function MyComponent() {
  const theme = useTheme();
  return <View style={{ backgroundColor: theme.colors.surface }} />;
}

Components

Button

npx @kyaw-min-khant/rnc add button
import { Button } from '@/components/ui/Button';

<Button title="Submit" onPress={() => {}} />
<Button title="Secondary" onPress={() => {}} variant="secondary" />
<Button title="Outline" onPress={() => {}} variant="outline" />
<Button title="Ghost" onPress={() => {}} variant="ghost" />
<Button title="Loading" onPress={() => {}} loading />
<Button title="Disabled" onPress={() => {}} disabled />

| Prop | Type | Default | Description | |---|---|---|---| | title | string | — | Button label | | onPress | () => void | — | Press handler | | variant | 'primary' \| 'secondary' \| 'outline' \| 'ghost' | 'primary' | Visual style | | disabled | boolean | false | Disables interaction | | loading | boolean | false | Shows spinner, disables interaction | | style | ViewStyle | — | Extra container styles | | textStyle | TextStyle | — | Extra text styles | | className | string | — | NativeWind classes | | textClassName | string | — | NativeWind text classes |


Input

npx @kyaw-min-khant/rnc add input
import { Input } from '@/components/ui/Input';

<Input label="Email" placeholder="[email protected]" />
<Input label="Filled" variant="filled" placeholder="..." />
<Input label="Underlined" variant="underlined" placeholder="..." />
<Input label="With error" placeholder="..." error="This field is required" />
<Input
  label="With icons"
  placeholder="Search..."
  leftIcon={<SearchIcon />}
  rightIcon={<CloseIcon />}
/>

| Prop | Type | Default | Description | |---|---|---|---| | label | string | — | Label above the input | | error | string | — | Error message below the input | | variant | 'outlined' \| 'filled' \| 'underlined' | 'outlined' | Border style | | leftIcon | ReactNode | — | Icon on the left side | | rightIcon | ReactNode | — | Icon on the right side | | containerStyle | ViewStyle | — | Outer wrapper styles | | inputStyle | TextStyle | — | Inner TextInput styles | | labelStyle | TextStyle | — | Label text styles | | errorStyle | TextStyle | — | Error text styles | | className | string | — | NativeWind classes | | ...TextInputProps | | | All React Native TextInput props are supported |


Badge

npx @kyaw-min-khant/rnc add badge
import { Badge } from '@/components/ui/Badge';

<Badge label="New" color="primary" />
<Badge label="Error" color="error" variant="outline" />
<Badge count={5} color="error" />
<Badge count={120} maxCount={99} color="primary" />
<Badge label="Large" color="success" size="large" />

| Prop | Type | Default | Description | |---|---|---|---| | label | string | — | Text to display | | count | number | — | Numeric count (use instead of label) | | maxCount | number | 99 | Truncates count above this value (e.g. "99+") | | variant | 'filled' \| 'outline' | 'filled' | Visual style | | color | 'primary' \| 'secondary' \| 'error' \| 'success' \| 'warning' | 'primary' | Color palette | | size | 'small' \| 'medium' \| 'large' | 'medium' | Badge size | | style | ViewStyle | — | Extra container styles | | textStyle | TextStyle | — | Extra text styles | | className | string | — | NativeWind classes |


Dropdown

npx @kyaw-min-khant/rnc add dropdown
import { Dropdown } from '@/components/ui/Dropdown';

const options = [
  { label: 'Option A', value: 'a' },
  { label: 'Option B', value: 'b' },
  { label: 'Disabled', value: 'c', disabled: true },
];

<Dropdown
  label="Choose one"
  placeholder="Select..."
  options={options}
  value={selected}
  onSelect={setSelected}
  isSearchable
/>

| Prop | Type | Default | Description | |---|---|---|---| | options | DropdownOption[] | — | List of { label, value, disabled? } | | value | T \| null | — | Currently selected value (controlled) | | onSelect | (value: T) => void | — | Called when an option is selected | | onChange | (value: T) => void | — | Same as onSelect, provided for API consistency | | placeholder | string | 'Select...' | Placeholder text | | placeholderTextColor | string | — | Color of the placeholder and search input placeholder | | label | string | — | Label above the trigger | | isSearchable | boolean | true | Shows a search input in the menu | | disabled | boolean | false | Disables the trigger | | dropDownPosition | 'top' \| 'bottom' | 'bottom' | Menu open direction | | dropDownLeftIcon | ReactNode | — | Icon on the left of the trigger | | dropDownRightIcon | ReactNode | — | Icon on the right of the trigger | | renderItem | (item, meta) => ReactNode | — | Custom option renderer | | style | ViewStyle | — | Outer wrapper styles | | dropDownStyle | ViewStyle | — | Trigger button styles | | menuStyle | ViewStyle | — | Dropdown menu container styles | | optionStyle | ViewStyle | — | Individual option row styles | | optionTextStyle | TextStyle | — | Option label text styles | | selectedOptionStyle | ViewStyle | — | Styles applied to the selected option row | | selectedOptionTextStyle | TextStyle | — | Styles applied to the selected option's text | | labelStyle | TextStyle | — | Label text styles | | className | string | — | NativeWind classes for the wrapper | | dropDownClassName | string | — | NativeWind classes for the trigger |


FloatingActionButton

npx @kyaw-min-khant/rnc add floating-action-button
import { FloatingActionButton } from '@/components/ui/FloatingActionButton';

// Simple FAB
<FloatingActionButton
  icon={<PlusIcon />}
  onPress={() => {}}
/>

// Speed dial
<FloatingActionButton
  icon={<PlusIcon />}
  onPress={() => {}}
  actions={[
    { icon: <EditIcon />, label: 'Edit', onPress: () => {} },
    { icon: <ShareIcon />, label: 'Share', onPress: () => {} },
  ]}
/>

// Extended FAB with label
<FloatingActionButton
  icon={<PlusIcon />}
  label="Create"
  onPress={() => {}}
/>

| Prop | Type | Default | Description | |---|---|---|---| | onPress | () => void | — | Press handler (or toggle for speed dial) | | icon | ReactNode | — | Icon inside the FAB | | label | string | — | Shows an extended FAB with text | | actions | FABAction[] | [] | Speed-dial actions above the FAB | | position | 'bottom-right' \| 'bottom-left' \| 'bottom-center' | 'bottom-right' | Screen position | | size | 'small' \| 'medium' \| 'large' | 'medium' | FAB size | | color | string | theme.colors.primary | Background color | | iconColor | string | theme.colors.onPrimary | Icon/label color | | disabled | boolean | false | Disables the FAB | | style | ViewStyle | — | Extra styles |


Skeleton

Loading placeholder with a pulsing opacity animation (built on React Native's core Animated API — no extra dependency required). Comes in two shapes.

npx @kyaw-min-khant/rnc add skeleton
import { Skeleton } from '@/components/ui/Skeleton';

// Rectangular skeleton, e.g. a text line
<Skeleton width="60%" height={16} />

// Circle skeleton, e.g. an avatar placeholder
<Skeleton variant="circle" size={48} />

| Prop | Type | Default | Description | |---|---|---|---| | variant | 'rectangular' \| 'circle' | 'rectangular' | Shape of the skeleton | | width | number \| string | '100%' | Width — number (px) or percentage string like '60%' (rectangular only) | | height | number \| string | 16 | Height — number (px) or percentage string (rectangular only) | | size | number | 40 | Diameter in px (circle only) | | borderRadius | number | theme.roundness | Corner radius (rectangular only) | | color | string | #9c9c9d | Fill color |


DateTimePicker

A fully custom, inline date & time picker with no external calendar dependency. It renders directly in your layout — no modal or trigger field — and steps through month → day → time.

npx @kyaw-min-khant/rnc add date-time-picker
import { DateTimePicker } from '@/components/ui/DateTimePicker';

const [date, setDate] = useState(new Date());

// Full date + time (default)
<DateTimePicker value={date} onChange={setDate} />

// Date only
<DateTimePicker value={date} onChange={setDate} mode="date" />

// Time only, 12-hour with AM/PM
<DateTimePicker value={date} onChange={setDate} mode="time" is24Hour={false} />

// Bounded range, 15-minute steps, week starts on Monday
<DateTimePicker
  value={date}
  onChange={setDate}
  minimumDate={new Date()}
  maximumDate={new Date(2027, 0, 1)}
  minuteInterval={15}
  firstDayOfWeek={1}
/>

| Prop | Type | Default | Description | |---|---|---|---| | value | Date \| null | new Date() | Selected date/time | | onChange | (date: Date) => void | — | Fires whenever the year, month, day, hour, minute or AM/PM changes | | mode | 'date' \| 'time' \| 'datetime' | 'datetime' | Which parts of the date are selectable | | minimumDate | Date | — | Earliest selectable day | | maximumDate | Date | — | Latest selectable day | | is24Hour | boolean | true | 24-hour hour wheel instead of 12-hour + AM/PM | | minuteInterval | 1 \| 5 \| 10 \| 15 \| 30 | 1 | Step between selectable minutes | | firstDayOfWeek | 0 \| 1 | 0 | Week starts on Sunday (0) or Monday (1) | | color | string | theme.colors.primary | Accent color for the selected day/month and wheel highlight | | style | ViewStyle | — | Extra container styles | | className | string | — | NativeWind classes |

Selecting a day in "datetime" mode automatically advances to the time step. Tapping the month/year header on the day grid jumps back to the month picker.


Utilities

cn()

Merge class names conditionally for NativeWind. Added automatically alongside any component that needs it (e.g. Dropdown), or on its own:

npx @kyaw-min-khant/rnc add utils
import { cn } from '@/lib/cn';

<View className={cn('p-4 rounded', isActive && 'bg-blue-500', className)} />

License

UNLICENSED — private package by KMK-MOBILE.