Files
gh-cskiro-claudex-devops-tools/skills/react-project-scaffolder/modes/mobile-mode.md
2025-11-29 18:16:54 +08:00

21 KiB

Mobile Mode - Expo + React Native

Mode Overview

Purpose: Cross-platform mobile applications with production-ready tooling

Target Users:

  • Building iOS and Android apps from single codebase
  • Mobile-first products and services
  • Teams wanting native performance with React
  • Startups needing fast mobile development
  • Enterprise mobile applications

Setup Time: ~60 seconds (after npm install)

Philosophy: Native performance, React developer experience, production standards from day one.


Tech Stack

Framework:
  - Expo SDK 50+ (managed workflow)
  - React Native (latest stable)
  - TypeScript 5+ (strict mode)

Navigation:
  - Expo Router (file-based routing)
  - React Navigation (under the hood)

Testing:
  - Jest (React Native default)
  - React Native Testing Library
  - 80% coverage threshold

Code Quality:
  - ESLint (React Native rules)
  - Prettier (consistent formatting)
  - Husky (pre-commit hooks)
  - lint-staged (staged files only)

Build & Deploy:
  - EAS Build (cloud builds - optional)
  - EAS Submit (app store submission - optional)
  - OTA Updates (instant updates)

Standards:
  - Conventional Commits
  - TypeScript strict mode
  - Testing Trophy approach

Workflow

Step 1: Validate Prerequisites

# Check Node.js version (>= 18)
node --version

# Check npm version (>= 9)
npm --version

# Check git is installed
git --version

# Check if Expo CLI is needed (will be installed via npx)
echo "Expo CLI will be used via npx"

If validation fails: Provide upgrade instructions

Step 2: Ask Configuration Questions

Only essential questions with smart defaults:

Question 1: Project Name

  • "What should I name your mobile project?"
  • Validation: kebab-case or PascalCase, 3-50 chars
  • Example: my-awesome-app or MyAwesomeApp

Question 2: Navigation Setup

  • "Include Expo Router for navigation?"
  • Default: YES
  • Explain: "File-based routing like Next.js. Recommended for most apps."

Question 3: Testing Setup

  • "Include testing infrastructure? (Jest + RN Testing Library)"
  • Default: YES
  • Explain: "Recommended for production apps. Connor's 80% coverage standard."

Question 4: EAS Cloud Builds

  • "Set up EAS for cloud builds and app store submission?"
  • Default: NO (can add later)
  • Explain: "Requires Expo account. Can configure later when ready to deploy."

Step 3: Scaffold with Expo

npx create-expo-app@latest {project-name} --template blank-typescript
cd {project-name}

Why Expo?

  • Used by Instagram, Discord, Shopify
  • Fastest mobile development experience
  • Built-in access to native APIs
  • Over-the-air (OTA) updates
  • Managed workflow (easier) or bare workflow (more control)

Step 4: Install Expo Router (if selected)

npx expo install expo-router react-native-safe-area-context react-native-screens expo-linking expo-constants expo-status-bar

Update package.json:

{
  "main": "expo-router/entry"
}

Create app/_layout.tsx:

import { Stack } from 'expo-router';

export default function RootLayout() {
  return <Stack />;
}

Create app/index.tsx:

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

export default function Home() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Welcome to {project-name}</Text>
      <Text style={styles.subtitle}>
        Edit app/index.tsx to get started
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 12,
  },
  subtitle: {
    fontSize: 16,
    color: '#666',
    textAlign: 'center',
  },
});

Update app.json:

{
  "expo": {
    "name": "{project-name}",
    "slug": "{project-name}",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "automatic",
    "scheme": "{project-name}",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "assetBundlePatterns": ["**/*"],
    "ios": {
      "supportsTablet": true,
      "bundleIdentifier": "com.{username}.{project-name}"
    },
    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      },
      "package": "com.{username}.{project-name}"
    },
    "web": {
      "bundler": "metro",
      "favicon": "./assets/favicon.png"
    },
    "plugins": ["expo-router"]
  }
}

Step 5: Configure TypeScript (Strict Mode)

Update tsconfig.json:

{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitReturns": true,
    "noImplicitOverride": true,
    "paths": {
      "@/*": ["./*"]
    }
  },
  "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"],
  "exclude": ["node_modules"]
}

Step 6: Set Up Testing (if selected)

6.1 Install Dependencies

npm install -D jest jest-expo @testing-library/react-native @testing-library/jest-native @types/jest

6.2 Create jest.config.js

module.exports = {
  preset: 'jest-expo',
  setupFilesAfterEnv: ['<rootDir>/__tests__/setup.ts'],
  transformIgnorePatterns: [
    'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)',
  ],
  collectCoverageFrom: [
    '**/*.{ts,tsx}',
    '!**/coverage/**',
    '!**/node_modules/**',
    '!**/babel.config.js',
    '!**/jest.config.js',
    '!**/__tests__/**',
  ],
  coverageThreshold: {
    global: {
      lines: 80,
      functions: 80,
      branches: 80,
      statements: 80,
    },
  },
};

6.3 Create Test Setup

__tests__/setup.ts:

import '@testing-library/jest-native/extend-expect';

// Mock Expo modules
jest.mock('expo-font');
jest.mock('expo-asset');

// Silence console warnings in tests
global.console = {
  ...console,
  warn: jest.fn(),
  error: jest.fn(),
};

6.4 Create Example Test

__tests__/App.test.tsx:

import React from 'react';
import { render, screen } from '@testing-library/react-native';
import Home from '../app/index';

describe('Home Screen', () => {
  it('should render welcome message when app loads', () => {
    render(<Home />);

    // Testing Trophy approach: Test user-visible behavior
    expect(screen.getByText(/welcome to/i)).toBeTruthy();
  });

  it('should display instructions for getting started', () => {
    render(<Home />);

    expect(screen.getByText(/edit app\/index.tsx/i)).toBeTruthy();
  });
});

6.5 Update package.json Scripts

{
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "test": "jest --passWithNoTests",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage",
    "lint": "eslint . --ext .ts,.tsx",
    "type-check": "tsc --noEmit"
  }
}

Step 7: Configure Code Quality Tools

7.1 Set Up ESLint

npm install -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react eslint-plugin-react-native

Create .eslintrc.js:

module.exports = {
  root: true,
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react/recommended',
    'plugin:react-native/all',
  ],
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint', 'react', 'react-native'],
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 2020,
    sourceType: 'module',
  },
  env: {
    'react-native/react-native': true,
  },
  rules: {
    // Connor's standards
    'no-console': 'warn',
    'no-var': 'error',
    'eqeqeq': ['error', 'always'],
    'prefer-const': 'error',
    '@typescript-eslint/no-unused-vars': [
      'error',
      { argsIgnorePattern: '^_' },
    ],
    '@typescript-eslint/no-explicit-any': 'error',
    'react/react-in-jsx-scope': 'off', // Not needed in React Native
    'react-native/no-inline-styles': 'warn',
  },
  settings: {
    react: {
      version: 'detect',
    },
  },
};

7.2 Add Prettier

Create .prettierrc:

{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 80,
  "tabWidth": 2,
  "useTabs": false
}

Install Prettier:

npm install -D prettier eslint-config-prettier

7.3 Set Up Husky + lint-staged

npx husky-init && npm install
npm install -D lint-staged

Update .husky/pre-commit:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged

Create .lintstagedrc.js:

module.exports = {
  '*.{ts,tsx}': [
    'eslint --fix',
    'prettier --write',
    () => 'tsc --noEmit',
    'jest --bail --findRelatedTests --passWithNoTests',
  ],
  '*.{json,md}': ['prettier --write'],
};

Step 8: Optional EAS Configuration (if selected)

# Install EAS CLI globally
npm install -g eas-cli

# Login to Expo
eas login

# Configure EAS Build
eas build:configure

This creates eas.json:

{
  "cli": {
    "version": ">= 5.9.0"
  },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal"
    },
    "preview": {
      "distribution": "internal",
      "ios": {
        "simulator": true
      }
    },
    "production": {
      "autoIncrement": true
    }
  },
  "submit": {
    "production": {}
  }
}

Step 9: Create Project Documentation

Update README.md:

# {project-name}

Cross-platform mobile application built with Expo and React Native.

## Features

- 📱 React Native (iOS + Android from single codebase)
- ⚡ Expo SDK 50+ (managed workflow)
- 🧭 Expo Router (file-based navigation)
- 🔷 TypeScript (strict mode)
- 🧪 Testing Trophy approach (Jest + RN Testing Library)
- ✅ 80% test coverage threshold
- 🎨 ESLint + Prettier
- 🪝 Husky pre-commit hooks
- 🚀 EAS Build & Submit (optional)

## Getting Started

### Prerequisites

- Node.js 18+
- npm 9+
- iOS Simulator (Mac only) or Android Emulator
- Expo Go app on physical device (optional)

### Installation

\`\`\`bash
npm install
\`\`\`

### Development

\`\`\`bash
npm start
\`\`\`

This opens the Expo Dev Server. From there:

- Press **i** to open iOS simulator
- Press **a** to open Android emulator
- Scan QR code with Expo Go app on your phone

Or run directly:

\`\`\`bash
npm run ios       # iOS simulator
npm run android   # Android emulator
npm run web       # Web browser
\`\`\`

## Project Structure

\`\`\`
app/                        # Expo Router (file-based routing)
  ├── _layout.tsx           # Root layout
  ├── index.tsx             # Home screen
  └── (tabs)/               # Tab navigation (if using)
components/                 # Reusable components
  ├── ui/                   # UI components
  └── features/             # Feature components
__tests__/                  # Test files
  ├── setup.ts              # Test configuration
  └── App.test.tsx          # Example test
assets/                     # Images, fonts, etc.
  ├── icon.png
  ├── splash.png
  └── adaptive-icon.png
\`\`\`

## Available Commands

### Development
- \`npm start\` - Start Expo dev server
- \`npm run ios\` - Run on iOS simulator
- \`npm run android\` - Run on Android emulator
- \`npm run web\` - Run in web browser

### Code Quality
- \`npm run lint\` - Lint code with ESLint
- \`npm run type-check\` - Check TypeScript types

### Testing
- \`npm test\` - Run all tests
- \`npm run test:watch\` - Run tests in watch mode
- \`npm run test:coverage\` - Run tests with coverage report

### Build & Deploy (if EAS configured)
- \`eas build --platform ios\` - Build for iOS
- \`eas build --platform android\` - Build for Android
- \`eas build --platform all\` - Build for both platforms
- \`eas submit --platform ios\` - Submit to App Store
- \`eas submit --platform android\` - Submit to Play Store

## Testing Strategy

This project follows the **Testing Trophy** approach:

- **70% Integration Tests**: User workflows and component interactions
- **20% Unit Tests**: Complex business logic
- **10% E2E Tests**: Critical user journeys (use Detox or Maestro)

### Writing Tests

Test file naming: \`[component-name].test.tsx\`

\`\`\`typescript
import { render, screen, fireEvent } from '@testing-library/react-native';

describe('LoginScreen', () => {
  it('should submit form when user enters valid credentials', () => {
    const onSubmit = jest.fn();
    render(<LoginScreen onSubmit={onSubmit} />);

    fireEvent.changeText(screen.getByPlaceholderText('Email'), 'test@example.com');
    fireEvent.changeText(screen.getByPlaceholderText('Password'), 'password123');
    fireEvent.press(screen.getByText('Login'));

    expect(onSubmit).toHaveBeenCalled();
  });
});
\`\`\`

## Navigation with Expo Router

File-based routing like Next.js:

\`\`\`
app/
  ├── _layout.tsx          → Root layout
  ├── index.tsx            → / (home)
  ├── about.tsx            → /about
  ├── users/
  │   ├── [id].tsx         → /users/:id (dynamic)
  │   └── index.tsx        → /users
  └── (tabs)/              → Tab navigation group
      ├── _layout.tsx
      ├── home.tsx
      └── profile.tsx
\`\`\`

Navigate programmatically:

\`\`\`typescript
import { router } from 'expo-router';

router.push('/about');
router.replace('/login');
router.back();
\`\`\`

## Environment Variables

Create \`.env\`:

\`\`\`env
API_URL=https://api.example.com
\`\`\`

Install and configure:

\`\`\`bash
npm install react-native-dotenv
\`\`\`

## Building for Production

### With EAS (Recommended)

\`\`\`bash
# Build for iOS (requires Apple Developer account)
eas build --platform ios --profile production

# Build for Android (requires Google Play Console account)
eas build --platform android --profile production

# Submit to stores
eas submit --platform ios
eas submit --platform android
\`\`\`

### Local Builds

\`\`\`bash
# iOS (requires Mac + Xcode)
npx expo run:ios --configuration Release

# Android
npx expo run:android --variant release
\`\`\`

## Over-the-Air (OTA) Updates

Expo allows instant updates without app store review:

\`\`\`bash
# Publish update to production
eas update --branch production --message "Bug fixes"

# Users get update on next app restart
\`\`\`

## Deployment Checklist

- [ ] Update \`version\` in app.json
- [ ] Test on physical iOS device
- [ ] Test on physical Android device
- [ ] Run full test suite: \`npm run test:coverage\`
- [ ] Check bundle size: \`npx expo export\`
- [ ] Update app screenshots
- [ ] Build for production: \`eas build --platform all\`
- [ ] Test production builds
- [ ] Submit to stores: \`eas submit --platform all\`

## Common Issues

### Metro bundler cache issues
\`\`\`bash
npm start -- --clear
\`\`\`

### iOS simulator not opening
\`\`\`bash
sudo xcode-select -s /Applications/Xcode.app
\`\`\`

### Android emulator issues
- Ensure Android Studio is installed
- Check emulator is running: \`adb devices\`

## Resources

- [Expo Documentation](https://docs.expo.dev)
- [Expo Router](https://docs.expo.dev/router/introduction/)
- [React Native Testing Library](https://callstack.github.io/react-native-testing-library/)
- [EAS Build](https://docs.expo.dev/build/introduction/)

## License

MIT

---

Built with [react-project-scaffolder](https://github.com/yourusername/react-project-scaffolder)

Step 10: Initialize Git

git init
git add .
git commit -m "feat: initial Expo + React Native setup with testing

- Expo SDK 50+ with managed workflow
- Expo Router for navigation
- TypeScript strict mode
- Jest + React Native Testing Library (80% coverage)
- ESLint + Prettier + Husky
- EAS configuration (optional)
- Comprehensive documentation"

Ensure .gitignore includes:

node_modules/
.expo/
dist/
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/

# macOS
.DS_Store

# Environment
.env
.env.local

# Testing
coverage/

Step 11: Verify Setup

# Verify all files
ls -la

# Check Expo installation
npx expo --version

# Verify tests run
npm test

# Start Expo dev server (verify it works)
npm start

Step 12: Provide User Instructions

Display to user:

✅ Mobile project "{project-name}" created successfully!

📁 Location: ./{project-name}

🚀 Next steps:

  1. cd {project-name}
  2. npm install
  3. npm start

Then:
  - Press 'i' for iOS simulator
  - Press 'a' for Android emulator
  - Scan QR code with Expo Go app on your phone

📚 What you have:
  ✓ Expo SDK 50+ (managed workflow)
  ✓ Expo Router (file-based navigation)
  ✓ TypeScript strict mode
  ✓ Jest + React Native Testing Library (80% coverage)
  ✓ ESLint + Prettier + Husky
  ✓ EAS Build configuration (if selected)
  ✓ OTA update support
  ✓ Comprehensive documentation

🧪 Test your setup:
  npm test           # Run all tests
  npm run lint       # Check code quality
  npm run type-check # Verify types

📱 Running on devices:
  - Install "Expo Go" app from App Store / Play Store
  - Scan QR code from terminal
  - See changes instantly with Fast Refresh

🔄 Pre-commit hooks active:
  - Linting (auto-fix)
  - Formatting (auto-format)
  - Type checking
  - Related tests run automatically

📦 Build for production (if EAS configured):
  eas build --platform all
  eas submit --platform all

💡 Tips:
  - Use Expo Router for navigation (like Next.js)
  - Test on physical devices early and often
  - Use EAS for cloud builds (no Xcode/Android Studio needed)
  - OTA updates allow instant bug fixes without app store review

📖 Documentation:
  - README.md - Complete guide with all commands
  - Expo docs: https://docs.expo.dev

🎯 Production-ready mobile development!

File Structure Output

{project-name}/
├── .husky/
│   └── pre-commit              # Pre-commit hooks
├── app/
│   ├── _layout.tsx             # Root layout (Expo Router)
│   └── index.tsx               # Home screen
├── assets/
│   ├── icon.png                # App icon
│   ├── splash.png              # Splash screen
│   └── adaptive-icon.png       # Android adaptive icon
├── components/
│   ├── ui/                     # UI components
│   └── features/               # Feature components
├── __tests__/
│   ├── setup.ts                # Test setup
│   └── App.test.tsx            # Example test
├── .eslintrc.js                # ESLint config
├── .prettierrc                 # Prettier config
├── .gitignore                  # Git ignore
├── .lintstagedrc.js            # lint-staged config
├── jest.config.js              # Jest config
├── tsconfig.json               # TypeScript config
├── app.json                    # Expo config
├── package.json                # Dependencies
├── eas.json                    # EAS Build config (if configured)
└── README.md                   # Documentation

Success Criteria

  • Expo project scaffolded successfully
  • Expo Router configured (if selected)
  • TypeScript strict mode enabled
  • Jest + RN Testing Library configured
  • Example test passes
  • ESLint + Prettier configured
  • Husky pre-commit hooks working
  • EAS configured (if selected)
  • README generated
  • Git initialized with commit
  • npm start opens Expo Dev Server
  • QR code displays for device testing
  • Setup time < 90 seconds (excluding npm install)

Troubleshooting

Issue: Expo CLI not found Solution: Use npx: npx expo start

Issue: Metro bundler cache issues Solution: Clear cache: npm start -- --clear

Issue: Tests fail with React Native module errors Solution: Check jest.config.js transformIgnorePatterns

Issue: iOS simulator won't open Solution: Set Xcode path: sudo xcode-select -s /Applications/Xcode.app

Issue: Android emulator not detected Solution: Check ADB: adb devices, ensure emulator is running

Issue: EAS build fails Solution: Check credentials and app config in app.json


Why This Tech Stack?

Expo over bare React Native:

  • Faster development (managed workflow)
  • Built-in access to native APIs
  • OTA updates (instant bug fixes)
  • Used by Instagram, Discord, Shopify
  • Easier for beginners, powerful for pros

Expo Router over React Navigation:

  • File-based routing (like Next.js)
  • Better TypeScript support
  • Deep linking built-in
  • Less boilerplate

EAS Build over local builds:

  • No need for Xcode/Android Studio
  • Cloud-based builds
  • Consistent environments
  • Easy team collaboration

Remember: This mode delivers native mobile performance with React developer experience. Production-ready with Connor's standards applied to mobile development.