react-native

React Native CLI Development Expert

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "react-native" with this command: npx skills add 0xkynz/codekit/0xkynz-codekit-react-native

React Native CLI Development Expert

Expert in React Native development with the bare CLI workflow, TypeScript, and native module integration. Specialized in building cross-platform mobile applications that require direct native code access.

When to Use

  • React Native CLI projects (bare workflow)

  • Apps requiring custom native modules

  • Projects with complex native integrations

  • Brownfield apps (React Native in existing native apps)

  • Apps needing fine-grained native control

For Expo managed workflow, use react-native-expo agent instead.

Technology Stack

Core

  • React Native CLI: Bare workflow setup

  • TypeScript: Strict typing and best practices

  • Metro: JavaScript bundler

  • Flipper: Debugging tool

UI/Styling

  • NativeWind: Tailwind CSS for React Native

  • React Native Reanimated: Smooth animations

  • React Native Gesture Handler: Touch interactions

  • React Native Vector Icons: Icon library

Navigation

  • React Navigation: Stack, Tab, Drawer navigators

  • React Native Screens: Native navigation primitives

Data & State

  • TanStack Query: Server state management

  • Zustand: Client state management

  • React Hook Form + Zod: Form handling

  • MMKV: Fast key-value storage

  • React Native Keychain: Secure storage

Native Integration

  • Turbo Modules: New Architecture native modules

  • Fabric: New Architecture renderer

  • CocoaPods: iOS dependency management

  • Gradle: Android build system

Project Structure

/my-react-native-app ├── /android/ # Android native project │ ├── /app/ │ │ ├── /src/ │ │ │ └── /main/ │ │ │ ├── /java/ # Java/Kotlin code │ │ │ └── /res/ # Android resources │ │ └── build.gradle │ ├── build.gradle │ ├── gradle.properties │ └── settings.gradle ├── /ios/ # iOS native project │ ├── /MyApp/ │ │ ├── AppDelegate.mm │ │ ├── Info.plist │ │ └── /Images.xcassets/ │ ├── Podfile │ └── MyApp.xcworkspace ├── /src/ │ ├── /components/ # Reusable components │ │ ├── /ui/ # Base UI (Button, Input, Card) │ │ ├── /forms/ # Form components │ │ └── /lists/ # List components │ ├── /features/ # Feature modules │ │ ├── /auth/ │ │ │ ├── /components/ │ │ │ ├── /hooks/ │ │ │ ├── /services/ │ │ │ └── index.ts │ │ └── /settings/ │ ├── /hooks/ # Custom hooks │ ├── /navigation/ # Navigation configuration │ │ ├── RootNavigator.tsx │ │ ├── AuthNavigator.tsx │ │ └── MainNavigator.tsx │ ├── /native/ # Native module bridges │ ├── /services/ # API services │ ├── /store/ # State management │ ├── /types/ # TypeScript types │ ├── /utils/ # Utilities │ ├── /constants/ # App constants │ └── /theme/ # Theme configuration ├── /assets/ # Images, fonts, etc. ├── App.tsx # App entry point ├── index.js # Native entry point ├── metro.config.js # Metro bundler config ├── babel.config.js ├── tsconfig.json ├── react-native.config.js # RN CLI config └── package.json

Code Standards

Component Pattern

import { View, Text, Pressable, StyleSheet } from "react-native"; import { forwardRef } from "react"; import { cn } from "@/utils/cn";

interface ButtonProps { variant?: "default" | "outline" | "ghost"; size?: "sm" | "md" | "lg"; onPress?: () => void; disabled?: boolean; className?: string; children: React.ReactNode; }

const Button = forwardRef<View, ButtonProps>( ({ variant = "default", size = "md", className, children, ...props }, ref) => { return ( <Pressable ref={ref} className={cn( "items-center justify-center rounded-lg", variants[variant], sizes[size], props.disabled && "opacity-50", className )} {...props} > <Text className={cn("font-medium", textVariants[variant])}> {children} </Text> </Pressable> ); } ); Button.displayName = "Button";

export { Button };

Custom Hook Pattern

import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";

export function useUsers() { return useQuery({ queryKey: ["users"], queryFn: () => userService.getAll(), }); }

export function useCreateUser() { const queryClient = useQueryClient();

return useMutation({ mutationFn: userService.create, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["users"] }); }, }); }

Form Pattern (React Hook Form + Zod)

import { View, TextInput, Text, Pressable } from "react-native"; import { useForm, Controller } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod";

const schema = z.object({ email: z.string().email(), password: z.string().min(8), });

type FormData = z.infer<typeof schema>;

export function LoginForm() { const { control, handleSubmit, formState: { errors } } = useForm<FormData>({ resolver: zodResolver(schema), });

const onSubmit = (data: FormData) => { // Handle submission };

return ( <View className="gap-4"> <Controller control={control} name="email" render={({ field: { onChange, onBlur, value } }) => ( <View> <TextInput className="border border-gray-300 rounded-lg px-4 py-3" placeholder="Email" onBlur={onBlur} onChangeText={onChange} value={value} keyboardType="email-address" autoCapitalize="none" /> {errors.email && ( <Text className="text-red-500 text-sm mt-1"> {errors.email.message} </Text> )} </View> )} /> <Controller control={control} name="password" render={({ field: { onChange, onBlur, value } }) => ( <View> <TextInput className="border border-gray-300 rounded-lg px-4 py-3" placeholder="Password" onBlur={onBlur} onChangeText={onChange} value={value} secureTextEntry /> {errors.password && ( <Text className="text-red-500 text-sm mt-1"> {errors.password.message} </Text> )} </View> )} /> <Pressable className="bg-blue-500 rounded-lg py-3 items-center" onPress={handleSubmit(onSubmit)} > <Text className="text-white font-semibold">Login</Text> </Pressable> </View> ); }

React Navigation Setup

// src/navigation/RootNavigator.tsx import { NavigationContainer } from "@react-navigation/native"; import { createNativeStackNavigator } from "@react-navigation/native-stack"; import { AuthNavigator } from "./AuthNavigator"; import { MainNavigator } from "./MainNavigator"; import { useAuth } from "@/hooks/useAuth";

export type RootStackParamList = { Auth: undefined; Main: undefined; };

const Stack = createNativeStackNavigator<RootStackParamList>();

export function RootNavigator() { const { isAuthenticated } = useAuth();

return ( <NavigationContainer> <Stack.Navigator screenOptions={{ headerShown: false }}> {isAuthenticated ? ( <Stack.Screen name="Main" component={MainNavigator} /> ) : ( <Stack.Screen name="Auth" component={AuthNavigator} /> )} </Stack.Navigator> </NavigationContainer> ); }

Tab Navigator

// src/navigation/MainNavigator.tsx import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; import Icon from "react-native-vector-icons/Ionicons"; import { HomeScreen } from "@/features/home/screens/HomeScreen"; import { ProfileScreen } from "@/features/profile/screens/ProfileScreen";

export type MainTabParamList = { Home: undefined; Profile: undefined; };

const Tab = createBottomTabNavigator<MainTabParamList>();

export function MainNavigator() { return ( <Tab.Navigator screenOptions={{ tabBarActiveTintColor: "#3b82f6", tabBarInactiveTintColor: "#9ca3af", }} > <Tab.Screen name="Home" component={HomeScreen} options={{ tabBarIcon: ({ color, size }) => ( <Icon name="home" size={size} color={color} /> ), }} /> <Tab.Screen name="Profile" component={ProfileScreen} options={{ tabBarIcon: ({ color, size }) => ( <Icon name="person" size={size} color={color} /> ), }} /> </Tab.Navigator> ); }

List with FlashList

import { FlashList } from "@shopify/flash-list"; import { View, Text, Pressable } from "react-native";

interface User { id: string; name: string; email: string; }

interface UsersListProps { users: User[]; onUserPress: (user: User) => void; }

export function UsersList({ users, onUserPress }: UsersListProps) { const renderItem = ({ item }: { item: User }) => ( <Pressable className="bg-white p-4 border-b border-gray-100" onPress={() => onUserPress(item)} > <Text className="font-semibold text-gray-900">{item.name}</Text> <Text className="text-gray-500 text-sm">{item.email}</Text> </Pressable> );

return ( <FlashList data={users} renderItem={renderItem} estimatedItemSize={72} keyExtractor={(item) => item.id} /> ); }

Native Module (Turbo Module)

// src/native/NativeCalculator.ts import { TurboModule, TurboModuleRegistry } from "react-native";

export interface Spec extends TurboModule { add(a: number, b: number): Promise<number>; multiply(a: number, b: number): number; }

export default TurboModuleRegistry.getEnforcing<Spec>("NativeCalculator");

// ios/NativeCalculator.mm #import "NativeCalculator.h"

@implementation NativeCalculator

RCT_EXPORT_MODULE()

  • (void)add:(double)a b:(double)b resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { resolve(@(a + b)); }

  • (NSNumber *)multiply:(double)a b:(double)b { return @(a * b); }

  • (std::shared_ptr<facebook::react::TurboModule>)getTurboModule: (const facebook::react::ObjCTurboModule::InitParams &)params { return std::make_shared<facebook::react::NativeCalculatorSpecJSI>(params); }

@end

// android/app/src/main/java/com/myapp/NativeCalculatorModule.kt package com.myapp

import com.facebook.react.bridge.* import com.facebook.react.module.annotations.ReactModule

@ReactModule(name = NativeCalculatorModule.NAME) class NativeCalculatorModule(reactContext: ReactApplicationContext) : NativeCalculatorSpec(reactContext) {

override fun getName() = NAME

override fun add(a: Double, b: Double, promise: Promise) {
    promise.resolve(a + b)
}

override fun multiply(a: Double, b: Double): Double {
    return a * b
}

companion object {
    const val NAME = "NativeCalculator"
}

}

Animation Pattern (Reanimated)

import Animated, { useSharedValue, useAnimatedStyle, withSpring, withTiming, } from "react-native-reanimated"; import { Pressable } from "react-native";

export function AnimatedButton({ children, onPress }) { const scale = useSharedValue(1);

const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }], }));

const handlePressIn = () => { scale.value = withSpring(0.95); };

const handlePressOut = () => { scale.value = withSpring(1); };

return ( <Pressable onPressIn={handlePressIn} onPressOut={handlePressOut} onPress={onPress} > <Animated.View style={animatedStyle}>{children}</Animated.View> </Pressable> ); }

Configuration

Metro Config

// metro.config.js const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");

const config = { transformer: { getTransformOptions: async () => ({ transform: { experimentalImportSupport: false, inlineRequires: true, }, }), }, };

module.exports = mergeConfig(getDefaultConfig(__dirname), config);

Babel Config

// babel.config.js module.exports = { presets: ["module:@react-native/babel-preset"], plugins: [ "nativewind/babel", "react-native-reanimated/plugin", [ "module-resolver", { root: ["./src"], alias: { "@": "./src", }, }, ], ], };

React Native Config

// react-native.config.js module.exports = { project: { ios: {}, android: {}, }, assets: ["./assets/fonts/"], };

iOS Podfile

ios/Podfile

require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

platform :ios, min_ios_version_supported prepare_react_native_project!

linkage = ENV['USE_FRAMEWORKS'] if linkage != nil Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green use_frameworks! :linkage => linkage.to_sym end

target 'MyApp' do config = use_native_modules!

use_react_native!( :path => config[:reactNativePath], :app_path => "#{Pod::Config.instance.installation_root}/.." )

post_install do |installer| react_native_post_install( installer, config[:reactNativePath], :mac_catalyst_enabled => false ) end end

Android Gradle

// android/app/build.gradle android { namespace "com.myapp" compileSdkVersion rootProject.ext.compileSdkVersion

defaultConfig {
    applicationId "com.myapp"
    minSdkVersion rootProject.ext.minSdkVersion
    targetSdkVersion rootProject.ext.targetSdkVersion
    versionCode 1
    versionName "1.0.0"
}

buildTypes {
    debug {
        signingConfig signingConfigs.debug
    }
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
        signingConfig signingConfigs.release
    }
}

}

dependencies { implementation("com.facebook.react:react-android")

if (hermesEnabled.toBoolean()) {
    implementation("com.facebook.react:hermes-android")
} else {
    implementation jscFlavor
}

}

Best Practices

Component Organization

  • Use feature-based folder structure

  • Colocate related code (components, hooks, types)

  • Use barrel exports (index.ts)

  • Keep components small and focused

State Management

  • Server state: TanStack Query

  • Client state: Zustand

  • Form state: React Hook Form

  • Navigation state: React Navigation

  • Persistent state: MMKV or Keychain

Performance

  • Use FlashList instead of FlatList for long lists

  • Avoid inline styles and functions in render

  • Use React.memo for expensive components

  • Enable Hermes engine for faster JS execution

  • Use the New Architecture (Fabric + Turbo Modules)

Native Code

  • Use Turbo Modules for new native modules

  • Keep native code minimal and focused

  • Handle platform differences gracefully

  • Test native code on both platforms

TypeScript

  • Define interfaces for all props

  • Type navigation params with ParamList

  • Use strict mode

  • Use Zod for runtime validation

Platform Handling

  • Use Platform.select() for platform-specific code

  • Create .ios.tsx and .android.tsx files when needed

  • Test on both platforms regularly

  • Handle keyboard avoidance properly

Accessibility

  • Add accessibilityLabel to interactive elements

  • Use accessibilityRole appropriately

  • Ensure adequate touch target sizes (44x44 minimum)

  • Support dynamic text sizes

Build & Release

  • Use Fastlane for automated builds

  • Configure proper signing for both platforms

  • Set up CI/CD pipelines

  • Test on real devices before release

Quick Setup Commands

Create new React Native CLI project

npx @react-native-community/cli@latest init MyApp cd MyApp

Install core dependencies

npm install @tanstack/react-query zustand npm install react-hook-form @hookform/resolvers zod

Install navigation

npm install @react-navigation/native @react-navigation/native-stack @react-navigation/bottom-tabs npm install react-native-screens react-native-safe-area-context

Install UI/Animation

npm install react-native-reanimated react-native-gesture-handler npm install nativewind tailwindcss npm install react-native-vector-icons

Install FlashList for performant lists

npm install @shopify/flash-list

Install storage

npm install react-native-mmkv react-native-keychain

iOS setup

cd ios && pod install && cd ..

Initialize NativeWind

npx tailwindcss init

Start development

npm run ios npm run android

Build Commands

iOS Debug

npm run ios

iOS Release

npm run ios -- --mode Release

Android Debug

npm run android

Android Release

cd android && ./gradlew assembleRelease

Clean builds

cd ios && xcodebuild clean && cd .. cd android && ./gradlew clean && cd ..

Link assets (fonts, etc.)

npx react-native-asset

Debugging

Start Metro bundler

npm start

Start with cache reset

npm start -- --reset-cache

Open Flipper for debugging

Download from https://fbflipper.com/

View logs

npx react-native log-ios npx react-native log-android

Open React DevTools

npx react-devtools

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

uiux-design-expert

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

react-native-expo

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

figma-make-website-builder

No summary provided by upstream source.

Repository SourceNeeds Review