React Native

Set up beUI Native

beUI Native components are plain source you copy into your app, like the web components. Do the setup below once per app. After that, adding a component is copying its files from its page. It works in Expo and in bare React Native.

Requirements

The components are built and tested against these versions.

1. Install the libraries

In an Expo app, expo install picks the versions that match your SDK. Uniwind and Tailwind come from npm.

Shellterminal
npx expo install react-native-reanimated react-native-worklets react-native-gesture-handler
npm install uniwind tailwindcss

Expo's Babel preset already includes the Worklets plugin that Reanimated 4 needs. In a bare React Native app, add it to babel.config.js yourself:

JSbabel.config.js
module.exports = {
  presets: ["module:@react-native/babel-preset"],
  // Reanimated 4 runs animations as worklets. Keep this plugin last.
  plugins: ["react-native-worklets/plugin"],
};

2. Set up Uniwind

Components are styled with Tailwind class names through Uniwind. Create global.css in the project root. It loads Tailwind, Uniwind and the beUI theme you add in the next step.

CSSglobal.css
@import "tailwindcss";
@import "uniwind";
@import "./src/styles/theme.css";

Then point Metro at it. withUniwindConfig must be the outermost wrapper if you use other Metro wrappers, and cssEntryFile must be a relative path.

JSmetro.config.js
const { getDefaultConfig } = require("expo/metro-config");
const { withUniwindConfig } = require("uniwind/metro");

const config = getDefaultConfig(__dirname);

// Uniwind must be the outermost wrapper.
module.exports = withUniwindConfig(config, {
  cssEntryFile: "./global.css",
  dtsFile: "./src/uniwind-types.d.ts",
});

In a bare React Native app, the default config comes from @react-native/metro-config instead:

JSmetro.config.js
const { getDefaultConfig } = require("@react-native/metro-config");
const { withUniwindConfig } = require("uniwind/metro");

const config = getDefaultConfig(__dirname);

// Uniwind must be the outermost wrapper.
module.exports = withUniwindConfig(config, {
  cssEntryFile: "./global.css",
  dtsFile: "./src/uniwind-types.d.ts",
});

Uniwind writes the class name types to dtsFile when Metro starts, so start it once before checking types. If TypeScript then can't find the global.css import, declare CSS files:

TSsrc/env.d.ts
declare module "*.css";

3. Add the theme tokens

Components use semantic colors like bg-primary and text-muted, defined for light and dark mode, so they follow the system appearance. Save this file as src/styles/theme.css, the path global.css imports, and change the values to match your brand.

CSSsrc/styles/theme.css
/* Matches the default beUI palette in ui-components/lib/themes.ts.
   Native surface maps to card; muted maps to muted-foreground. */
@layer theme {
  :root {
    @variant light {
      --color-background: oklch(99% 0 0);
      --color-surface: oklch(97% 0 0);
      --color-foreground: oklch(15% 0 0);
      --color-muted: oklch(50% 0 0);
      --color-border: oklch(15% 0 0 / 0.06);
      --color-border-strong: oklch(15% 0 0 / 0.16);
      --color-primary: #0285f7;
      --color-on-primary: #ffffff;
      --color-destructive: #dc2626;
      --color-success: #059669;
      --color-warning: #d97706;
    }
  }

  :root {
    @variant dark {
      --color-background: #151515;
      --color-surface: #1c1c1c;
      --color-foreground: oklch(96% 0 0);
      --color-muted: oklch(62% 0 0);
      --color-border: rgb(255 255 255 / 0.05);
      --color-border-strong: rgb(255 255 255 / 0.14);
      --color-primary: #0285f7;
      --color-on-primary: #ffffff;
      --color-destructive: #f87171;
      --color-success: #34d399;
      --color-warning: #fbbf24;
    }
  }
}

4. Wrap your app

Import global.css from your root component, not from the file that registers the app, or every style change reloads the whole app. Wrap the app in GestureHandlerRootView so swipeable and draggable components can receive gestures. With Expo Router, both go in the root layout:

TSXapp/_layout.tsx
import "../global.css";

import { Stack } from "expo-router";
import { GestureHandlerRootView } from "react-native-gesture-handler";

export default function RootLayout() {
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <Stack />
    </GestureHandlerRootView>
  );
}

In a bare app, do the same in App.tsx. For forms, also wrap the app in KeyboardProvider from react-native-keyboard-controller to keep focused fields above the keyboard.

5. Add a component

Run the beUI Native CLI from your app's root. It copies the component and the shared files it needs into src/ and installs any libraries you don't have yet.

Shellterminal
npx beui-native add tabs

To copy files by hand instead, open the component from the React Native components and use its Manual steps. Either way, keep the folder layout, since components import shared files by relative path. Tabs ends up like this:

TEXTyour app
global.css
metro.config.js
src/
  components/
    tabs/
      index.ts
      tabs.tsx
  hooks/
    use-reduced-motion.ts
  primitives/
    render-icon.tsx
  styles/
    motion.ts
    theme.css

Then use it like any other component:

TSXapp/index.tsx
import { Text } from "react-native";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../src/components/tabs";

export default function Screen() {
  return (
    <Tabs defaultValue="overview" className="p-4">
      <TabsList>
        <TabsTrigger value="overview">Overview</TabsTrigger>
        <TabsTrigger value="activity">Activity</TabsTrigger>
      </TabsList>
      <TabsContent value="overview">
        <Text className="text-foreground">High-level summary.</Text>
      </TabsContent>
      <TabsContent value="activity">
        <Text className="text-foreground">Recent events.</Text>
      </TabsContent>
    </Tabs>
  );
}

Tailwind finds class names in files under the folder that holds global.css. If it lives somewhere else, like app/, add an @source for the components:

CSSapp/global.css
@import "tailwindcss";
@import "uniwind";
@import "../src/styles/theme.css";

/* This file is in app/, so the components in src/ need an explicit source. */
@source "../src";

Try before you copy

Every React Native component page has a QR code that opens it in Expo Go (SDK 57), so you can feel the motion on your phone first. Coding agents can fetch any component with its install commands and files from https://beui.dev/native/r, or through the beUI MCP server. See the Agent Guide.

Every component follows the system Reduce Motion setting and takes a disableAnimation prop. The source is on GitHub.