Get the FREE Ultimate OpenClaw Setup Guide →

React Native Guidelines

npx machina-cli add skill YoniChechik/claude-code-config/react-native-guidelines --openclaw
Files (1)
SKILL.md
6.1 KB

React Native Component Guidelines

This skill provides conventions for React Native/Expo mobile development.

React Compiler

  • React Compiler is enabled and handles memoization automatically
  • NEVER use useMemo, useCallback, or React.memo
  • The compiler optimizes re-renders without manual intervention
  • Just write plain functions and let the compiler optimize

Directory Structure

apps/mobile/src/
├── components/
│   ├── ui/                     # Design system primitives (Button, Input, Card, etc.)
│   │   └── [ComponentName]/
│   │       ├── [ComponentName].tsx
│   │       ├── [ComponentName].test.tsx
│   │       ├── [ComponentName].stories.tsx
│   │       └── index.ts
│   └── common/                 # Shared business components (Avatar, MessageBubble, etc.)
├── features/                   # Feature-specific code
│   └── [feature-name]/
│       ├── components/
│       ├── hooks/
│       └── screens/
├── hooks/                      # App-wide shared hooks
├── screens/                    # Top-level screens (navigation entry points)
└── lib/                        # Utilities, constants, types

Component Structure

Every component must be a folder with:

  • ComponentName.tsx - Implementation
  • ComponentName.test.tsx - Unit tests (required)
  • ComponentName.stories.tsx - Storybook story (required for ui/common)
  • index.ts - Barrel export

Component Pattern

import { View, Text, Pressable } from 'react-native';

export interface ButtonProps {
  label: string;
  onPress: () => void;
  variant?: 'primary' | 'secondary';
  disabled?: boolean;
}

export function Button({ label, onPress, variant = 'primary', disabled = false }: ButtonProps) {
  return (
    <Pressable
      className={`px-4 py-2 rounded-lg ${variant === 'primary' ? 'bg-accent' : 'bg-background'} ${disabled ? 'opacity-50' : ''}`}
      onPress={onPress}
      disabled={disabled}
    >
      <Text className={`text-center font-medium ${variant === 'primary' ? 'text-background' : 'text-foreground'}`}>
        {label}
      </Text>
    </Pressable>
  );
}

Barrel File (index.ts)

export { Button } from './Button';
export type { ButtonProps } from './Button';

Rules

  • Named exports only (no default exports)
  • Props interface named [ComponentName]Props and exported
  • Keep components focused on presentation; extract logic to hooks

Styling

Use uniwind (NativeWind) classes only. StyleSheet.create() is FORBIDDEN.

RuleStatus
Use uniwind className propRequired
Inline classNamesRequired
Use theme colorsRequired
StyleSheet.create()Forbidden
Inline style propForbidden (except dynamic values)
Hardcoded color valuesForbidden

Theme Colors

Colors must come from theme in global.css:

ClassUsage
bg-background / text-backgroundMain background
bg-foreground / text-foregroundMain text
bg-accent / text-accentAccent/highlight

Never use hardcoded colors like bg-white, text-black, bg-blue-500.

Correct

<View className="flex-1 bg-background p-4">
  <Text className="text-lg font-bold text-foreground">Title</Text>
</View>

Incorrect

// Hardcoded colors
<View className="bg-white">

// StyleSheet.create
const styles = StyleSheet.create({ container: { flex: 1 } });

// Inline style
<View style={{ flex: 1, backgroundColor: 'white' }} />

Hooks

Hook TypeLocation
Component-specificInside component folder
Feature-sharedfeatures/[feature]/hooks/
App-widehooks/

Pattern

export interface UseAuthReturn {
  user: User | null;
  login: (email: string, password: string) => Promise<void>;
  isLoading: boolean;
}

export function useAuth(): UseAuthReturn {
  // implementation
}

Screens

  • Orchestrate components, no business logic
  • Data fetching in hooks, not screens
  • NO Storybook stories (use Maestro E2E)

Testing

Unit Tests (Required)

import { render, screen, fireEvent } from '@testing-library/react-native';
import { Button } from './Button';

describe('Button', () => {
  it('renders label correctly', () => {
    render(<Button label="Click me" onPress={() => {}} />);
    expect(screen.getByText('Click me')).toBeOnTheScreen();
  });

  it('calls onPress when pressed', () => {
    const onPress = jest.fn();
    render(<Button label="Click me" onPress={onPress} />);
    fireEvent.press(screen.getByText('Click me'));
    expect(onPress).toHaveBeenCalledTimes(1);
  });
});

E2E Tests (Maestro)

# .maestro/flows/auth/login.yaml
appId: com.sunsay.attune
---
- launchApp
- tapOn: "Email"
- inputText: "test@example.com"
- tapOn: "Sign In"
- assertVisible: "Welcome"

Storybook

Required for ui/ and common/ components:

import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  title: 'ui/Button',
  component: Button,
  args: {
    label: 'Button',
    onPress: () => console.log('pressed'),
  },
};

export default meta;
type Story = StoryObj<typeof Button>;

export const Primary: Story = { args: { variant: 'primary' } };
export const Disabled: Story = { args: { disabled: true } };

Naming Conventions

ItemConventionExample
Component folderPascalCaseButton/
Component filePascalCaseButton.tsx
Hook filecamelCaseuseAuth.ts
Feature folderkebab-casefeatures/user-profile/

Checklist

  • Component in correct location
  • All required files present
  • Props interface exported
  • Styled with uniwind only
  • Theme colors only (no hardcoded)
  • Unit tests cover main functionality
  • Storybook story (for ui/common)
  • testID for interactive elements

Source

git clone https://github.com/YoniChechik/claude-code-config/blob/main/skills/react-native-guidelines/SKILL.mdView on GitHub

Overview

Provides conventions for React Native/Expo mobile development, covering component structure, styling with NativeWind, hooks, testing, and Storybook. It enforces a folder-first architecture, named exports, and theme-driven styling to keep code consistent, scalable, and testable across apps.

How This Skill Works

Enforces a strict folder structure: apps/mobile/src/components/ui, common, features, hooks, screens, and lib. Each component lives in its own folder with ComponentName.tsx, ComponentName.test.tsx, ComponentName.stories.tsx, and index.ts, with barrel exports. Styling uses NativeWind className props exclusively and pulls colors from the theme in global.css; StyleSheet.create and hardcoded colors are forbidden. The React Compiler handles memoization automatically, so manual useMemo/useCallback/React.memo are avoided.

When to Use It

  • Starting a new UI component with consistent styling and behavior
  • Adding reusable UI primitives (Button, Input, Avatar) in ui/common
  • Implementing a feature-specific component with its own hooks
  • Preparing components for Storybook documentation and visual QA
  • Enforcing mobile app conventions in a new feature area

Quick Start

  1. Step 1: Create the component folder and add ComponentName.tsx, ComponentName.test.tsx, ComponentName.stories.tsx, and index.ts
  2. Step 2: Implement UI using NativeWind className props, and apply colors from global.css theme
  3. Step 3: Export the component via a named export, define [ComponentName]Props, and add tests and a Storybook story

Best Practices

  • Create a dedicated component folder with ComponentName.tsx, ComponentName.test.tsx, ComponentName.stories.tsx, and index.ts
  • Use NativeWind className props exclusively; avoid StyleSheet.create() and inline styles except for dynamic values
  • Always export named components and Props as [ComponentName]Props
  • Keep components presentation-focused; move business logic to hooks located in the appropriate folder
  • Provide unit tests and a Storybook story for every ui/common component

Example Use Cases

  • Button component with variant prop (primary/secondary) using theme colors
  • Avatar or MessageBubble components under apps/mobile/src/components/common
  • UI primitives under apps/mobile/src/components/ui (Button, Card, Input)
  • Storybook stories for a Button and a Card to document visuals
  • Feature-level hooks placed under features/[feature-name]/hooks and reused by components

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers