agentleFS
allanninalallanninal/claude-code-skills/skills/expo-skills/SKILL.md

expo-skills

Skill1 starsChanged 9 months agoMITView on GitHub

Expo and React Native development - app building, EAS deployment, SDK upgrades, and mobile best practices. Use when building mobile apps with Expo.

  • Reads credentials
  • Installs packages
Files
SKILL.md

Expo Development Skills

When to Use This Skill

  • Building React Native apps with Expo
  • Deploying with EAS (Expo Application Services)
  • Upgrading Expo SDK versions
  • Configuring app builds and submissions
  • Implementing mobile-specific features

Project Setup

Create New Project

# Create with latest template
npx create-expo-app@latest my-app

# With specific template
npx create-expo-app my-app --template tabs
npx create-expo-app my-app --template blank-typescript

# Start development
cd my-app
npx expo start

Project Structure

my-app/
├── app/                    # App Router (file-based routing)
│   ├── (tabs)/            # Tab navigator group
│   │   ├── index.tsx      # Home tab
│   │   ├── explore.tsx    # Explore tab
│   │   └── _layout.tsx    # Tab layout
│   ├── _layout.tsx        # Root layout
│   └── +not-found.tsx     # 404 page
├── assets/                # Static assets
├── components/            # Shared components
├── constants/             # Theme, config
├── hooks/                 # Custom hooks
├── app.json              # Expo config
├── eas.json              # EAS Build config
└── package.json

App Configuration

app.json / app.config.js

// app.config.js
export default {
  expo: {
    name: "My App",
    slug: "my-app",
    version: "1.0.0",
    orientation: "portrait",
    icon: "./assets/icon.png",
    scheme: "myapp",
    userInterfaceStyle: "automatic",
    splash: {
      image: "./assets/splash.png",
      resizeMode: "contain",
      backgroundColor: "#ffffff"
    },
    ios: {
      supportsTablet: true,
      bundleIdentifier: "com.company.myapp",
      buildNumber: "1",
      infoPlist: {
        NSCameraUsageDescription: "This app uses the camera to..."
      }
    },
    android: {
      adaptiveIcon: {
        foregroundImage: "./assets/adaptive-icon.png",
        backgroundColor: "#ffffff"
      },
      package: "com.company.myapp",
      versionCode: 1,
      permissions: ["CAMERA", "RECORD_AUDIO"]
    },
    plugins: [
      "expo-router",
      ["expo-camera", { cameraPermission: "Allow camera access" }],
      ["expo-notifications", { icon: "./assets/notification-icon.png" }]
    ],
    extra: {
      eas: { projectId: "your-project-id" },
      apiUrl: process.env.API_URL
    }
  }
};

EAS Build & Submit

Setup EAS

# Install EAS CLI
npm install -g eas-cli

# Login
eas login

# Configure project
eas build:configure

eas.json Configuration

{
  "cli": {
    "version": ">= 5.0.0"
  },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal",
      "ios": {
        "simulator": true
      }
    },
    "preview": {
      "distribution": "internal",
      "ios": {
        "simulator": false
      }
    },
    "production": {
      "autoIncrement": true
    }
  },
  "submit": {
    "production": {
      "ios": {
        "appleId": "your@email.com",
        "ascAppId": "1234567890",
        "appleTeamId": "ABCD1234"
      },
      "android": {
        "serviceAccountKeyPath": "./pc-api-key.json",
        "track": "internal"
      }
    }
  }
}

Build Commands

# Development build (for dev client)
eas build --profile development --platform ios
eas build --profile development --platform android

# Preview build (internal testing)
eas build --profile preview --platform all

# Production build
eas build --profile production --platform all

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

SDK Upgrades

Upgrade Process

# Check current version
npx expo --version

# Install upgrade tool
npx expo install expo@latest

# Or specific version
npx expo install expo@52

# Fix dependencies
npx expo install --fix

# Check for issues
npx expo-doctor

Common Upgrade Issues

// Check for deprecated APIs
// SDK 52 changes:
// - expo-app-loading removed, use expo-splash-screen
// - Constants.manifest deprecated, use Constants.expoConfig

// Before (SDK 51)
import Constants from 'expo-constants';
const apiUrl = Constants.manifest?.extra?.apiUrl;

// After (SDK 52+)
import Constants from 'expo-constants';
const apiUrl = Constants.expoConfig?.extra?.apiUrl;

Common Patterns

Navigation (Expo Router)

// app/_layout.tsx
import { Stack } from 'expo-router';

export default function RootLayout() {
  return (
    <Stack>
      <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      <Stack.Screen name="modal" options={{ presentation: 'modal' }} />
    </Stack>
  );
}

// Navigate programmatically
import { router } from 'expo-router';

router.push('/details/123');
router.replace('/home');
router.back();

Authentication Flow

// app/_layout.tsx
import { useAuth } from '@/hooks/useAuth';
import { Redirect, Stack } from 'expo-router';

export default function AppLayout() {
  const { user, isLoading } = useAuth();

  if (isLoading) {
    return <SplashScreen />;
  }

  if (!user) {
    return <Redirect href="/login" />;
  }

  return <Stack />;
}

Secure Storage

import * as SecureStore from 'expo-secure-store';

// Store token
await SecureStore.setItemAsync('authToken', token);

// Retrieve token
const token = await SecureStore.getItemAsync('authToken');

// Delete token
await SecureStore.deleteItemAsync('authToken');

Push Notifications

import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';

async function registerForPushNotifications() {
  if (!Device.isDevice) {
    console.log('Push notifications require physical device');
    return;
  }

  const { status: existingStatus } = await Notifications.getPermissionsAsync();
  let finalStatus = existingStatus;

  if (existingStatus !== 'granted') {
    const { status } = await Notifications.requestPermissionsAsync();
    finalStatus = status;
  }

  if (finalStatus !== 'granted') {
    return;
  }

  const token = await Notifications.getExpoPushTokenAsync({
    projectId: Constants.expoConfig?.extra?.eas?.projectId,
  });

  return token.data;
}

Camera & Image Picker

import * as ImagePicker from 'expo-image-picker';

async function pickImage() {
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ImagePicker.MediaTypeOptions.Images,
    allowsEditing: true,
    aspect: [4, 3],
    quality: 0.8,
  });

  if (!result.canceled) {
    return result.assets[0].uri;
  }
}

async function takePhoto() {
  const { status } = await ImagePicker.requestCameraPermissionsAsync();
  if (status !== 'granted') return;

  const result = await ImagePicker.launchCameraAsync({
    allowsEditing: true,
    quality: 0.8,
  });

  if (!result.canceled) {
    return result.assets[0].uri;
  }
}

Performance Tips

// Use memo for expensive components
const MemoizedList = React.memo(ExpensiveList);

// Use FlashList instead of FlatList
import { FlashList } from "@shopify/flash-list";

<FlashList
  data={items}
  renderItem={renderItem}
  estimatedItemSize={100}
/>

// Optimize images
import { Image } from 'expo-image';

<Image
  source={uri}
  placeholder={blurhash}
  contentFit="cover"
  transition={200}
/>

Debugging

# Open developer menu
# iOS Simulator: Cmd + D
# Android Emulator: Cmd + M

# View logs
npx expo start --clear

# Debug with React DevTools
npx react-devtools

Checklist

  • Configure app.json with correct identifiers
  • Set up EAS for builds
  • Add required permissions
  • Configure splash screen and icons
  • Set up push notifications
  • Test on physical devices
  • Configure environment variables
  • Set up error monitoring

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.