jutsu-expo

Validation and quality enforcement for Expo React Native projects with config, router, modules, updates, and build skills.

View on GitHub
Author The Bushido Collective
Namespace @TheBushidoCollective/han
Category Technique
Version 1.0.0
Stars 66
Downloads 6
self.md verified
Table of content

Validation and quality enforcement for Expo React Native projects with config, router, modules, updates, and build skills.

Installation

npx claude-plugins install @TheBushidoCollective/han/jutsu-expo

Contents

Folders: skills

Files: CHANGELOG.md, README.md

Documentation

Validation and quality enforcement for Expo React Native projects.

What This Jutsu Provides

Skills

This jutsu provides the following skills:

Installation

Install with npx (no installation required):

han plugin install jutsu-expo

Requirements

Why Expo?

Expo is a framework and platform for React Native that makes mobile development faster and easier:

Features Covered by Skills

Configuration (app.json/app.config.ts)

Expo Router

Expo Modules (SDK)

OTA Updates

EAS Build

Quick Start Examples

Create New Expo App

npx create-expo-app@latest my-app
cd my-app

Basic Expo Router App

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

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

// app/index.tsx
import { Link } from 'expo-router';
import { View, Text } from 'react-native';

export default function Home() {
  return (
    <View>
      <Text>Home Screen</Text>
      <Link href="/details">Go to Details</Link>
    </View>
  );
}

// app/details.tsx
export default function Details() {
  return (
    <View>
      <Text>Details Screen</Text>
    </View>
  );
}

Using Expo Modules

import { Camera } from 'expo-camera';
import * as Location from 'expo-location';
import * as Notifications from 'expo-notifications';

// Request permissions and use modules
const [cameraPermission] = Camera.useCameraPermissions();

Configure for Production

// app.config.ts
export default {
  name: 'MyApp',
  slug: 'my-app',
  version: '1.0.0',
  extra: {
    apiUrl: process.env.API_URL,
  },
  plugins: [
    'expo-router',
    'expo-camera',
    'expo-location',
  ],
};

Build with EAS

# Install EAS CLI
npm install -g eas-cli

# Login
eas login

# Configure project
eas build:configure

# Build for iOS
eas build --platform ios --profile production

# Build for Android
eas build --platform android --profile production

Project Structure

Recommended structure for Expo apps with Expo Router:

my-app/
├── app/                  # Routes (file-based routing)
│   ├── (tabs)/          # Tab navigation group
│   │   ├── _layout.tsx
│   │   ├── index.tsx
│   │   └── profile.tsx
│   ├── _layout.tsx      # Root layout
│   └── index.tsx        # Home route
├── components/          # Reusable components
├── hooks/               # Custom hooks
├── utils/               # Utility functions
├── assets/              # Images, fonts, etc.
├── app.json             # Expo configuration
├── eas.json             # EAS Build configuration
├── package.json
└── tsconfig.json

Common Expo Modules

Essential modules for most Expo apps:

…(truncated)

Included Skills

This plugin includes 5 skill definitions:

expo-build

Use when building and deploying Expo apps with EAS Build. Covers build configuration, development builds, production builds, and app store submission.

View skill definition

Expo Build with EAS

Use this skill when building and deploying Expo applications using EAS (Expo Application Services) Build.

Key Concepts

EAS Build Configuration

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

Build Commands

# Development build
eas build --profile development --platform ios
eas build --profile development --platform android

# Preview build
eas build --profile preview --platform all

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

# Local build
eas build --profile production --platform ios --local

Development Builds

{
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal",
      "env": {
        "NODE_ENV": "development"
      }
    }
  }
}

Best Practices

Environment Variables

// eas.json
{
  "build": {
    "production": {
      "env": {
        "APP_ENV": "production",
        "API_URL": "https://api.myapp.com"
      }
    },
    "staging": {
      "env": {
        "APP_ENV": "staging",
        "API_URL": "https://staging-api.myapp.com"
      }
    }
  }
}

Secrets Management

# Set sec

...(truncated)

</details>

### expo-config

> Use when configuring Expo apps with app.json, app.config.js, and EAS configuration. Covers app metadata, plugins, build configuration, and environment variables.

<details>
<summary>View skill definition</summary>

# Expo Configuration

Use this skill when configuring Expo applications using app.json, app.config.js/ts, and EAS (Expo Application Services) configuration files.

## Key Concepts

### app.json Configuration

Basic static configuration:

```json
{
  "expo": {
    "name": "MyApp",
    "slug": "my-app",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "automatic",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "assetBundlePatterns": [
      "**/*"
    ],
    "ios": {
      "supportsTablet": true,
      "bundleIdentifier": "com.mycompany.myapp",
      "buildNumber": "1"
    },
    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      },
      "package": "com.mycompany.myapp",
      "versionCode": 1
    },
    "web": {
      "favicon": "./assets/favicon.png"
    }
  }
}

Dynamic Configuration (app.config.js)

Use JavaScript for dynamic configuration:

export default ({ config }) => ({
  ...config,
  name: process.env.APP_NAME || 'MyApp',
  slug: 'my-app',
  version: '1.0.0',
  extra: {
    apiUrl: process.env.API_URL,
    environment: process.env.NODE_ENV,
  },
  ios: {
    bundleIdentifier:
      process.env.NODE_ENV === 'production'
        ? 'com.mycompany.myapp'
        : 'com.mycompany.myapp.dev',
  },
  android: {
    packag

...(truncated)

</details>

### expo-modules

> Use when working with Expo SDK modules for camera, location, notifications, file system, and other device APIs. Covers permissions, configurations, and best practices.

<details>
<summary>View skill definition</summary>

# Expo Modules

Use this skill when working with Expo's extensive SDK modules for accessing device features and native functionality.

## Key Concepts

### Camera

```tsx
import { Camera, CameraType } from 'expo-camera';
import { useState } from 'react';
import { Button, View } from 'react-native';

export default function CameraScreen() {
  const [permission, requestPermission] = Camera.useCameraPermissions();
  const [type, setType] = useState(CameraType.back);

  if (!permission?.granted) {
    return (
      <View>
        <Button title="Grant Permission" onPress={requestPermission} />
      </View>
    );
  }

  return (
    <Camera style={{ flex: 1 }} type={type}>
      <Button
        title="Flip Camera"
        onPress={() =>
          setType(type === CameraType.back ? CameraType.front : CameraType.back)
        }
      />
    </Camera>
  );
}

Location

import * as Location from 'expo-location';
import { useEffect, useState } from 'react';

export function useLocation() {
  const [location, setLocation] = useState<Location.LocationObject | null>(null);

  useEffect(() => {
    (async () => {
      const { status } = await Location.requestForegroundPermissionsAsync();
      if (status !== 'granted') return;

      const loc = await Location.getCurrentPositionAsync({});
      setLocation(loc);
    })();
  }, []);

  return location;
}

Notifications

import * as Notifications from 'expo-notifications';
import { useEffect } from 'react';



...(truncated)

</details>

### expo-router

> Use when implementing file-based routing in Expo with Expo Router. Covers app directory structure, navigation, layouts, dynamic routes, and deep linking.

<details>
<summary>View skill definition</summary>

# Expo Router

Use this skill when implementing file-based routing with Expo Router, the recommended navigation solution for Expo apps.

## Key Concepts

### File-Based Routing

Routes are defined by file structure:

app/ _layout.tsx # Root layout index.tsx # / route about.tsx # /about route (tabs)/ # Group (not in URL) _layout.tsx # Tabs layout home.tsx # /home profile.tsx # /profile users/ [id].tsx # /users/:id dynamic route index.tsx # /users route


### Basic Routes

```tsx
// app/index.tsx
import { View, Text } from 'react-native';
import { Link } from 'expo-router';

export default function Home() {
  return (
    <View>
      <Text>Home Screen</Text>
      <Link href="/about">Go to About</Link>
    </View>
  );
}

// app/about.tsx
export default function About() {
  return (
    <View>
      <Text>About Screen</Text>
    </View>
  );
}

Layouts

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

export default function RootLayout() {
  return (
    <Stack>
      <Stack.Screen name="index" options={{ title: 'Home' }} />
      <Stack.Screen name="about" options={{ title: 'About' }} />
    </Stack>
  );
}

Tab Navigation

// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';

export default function TabLayout() {
  return (
    <Tabs>
      <Tabs.Screen
       

...(truncated)

</details>

### expo-updates

> Use when implementing over-the-air (OTA) updates with Expo Updates. Covers update configuration, checking for updates, and update strategies.

<details>
<summary>View skill definition</summary>

# Expo Updates

Use this skill when implementing over-the-air (OTA) updates to deploy JavaScript and asset updates without app store releases.

## Key Concepts

### Configuration

```json
// app.json
{
  "expo": {
    "updates": {
      "enabled": true,
      "checkAutomatically": "ON_LOAD",
      "fallbackToCacheTimeout": 0,
      "url": "https://u.expo.dev/[project-id]"
    },
    "runtimeVersion": {
      "policy": "sdkVersion"
    }
  }
}

Checking for Updates

import * as Updates from 'expo-updates';
import { useEffect, useState } from 'react';
import { View, Text, Button } from 'react-native';

export default function App() {
  const [updateAvailable, setUpdateAvailable] = useState(false);

  useEffect(() => {
    async function checkForUpdates() {
      if (!__DEV__) {
        const update = await Updates.checkForUpdateAsync();
        setUpdateAvailable(update.isAvailable);
      }
    }
    checkForUpdates();
  }, []);

  const handleUpdate = async () => {
    const { isNew } = await Updates.fetchUpdateAsync();
    if (isNew) {
      await Updates.reloadAsync();
    }
  };

  if (updateAvailable) {
    return (
      <View>
        <Text>Update Available!</Text>
        <Button title="Update Now" onPress={handleUpdate} />
      </View>
    );
  }

  return <View>{/* Your app */}</View>;
}

Runtime Versions

// app.config.ts
export default {
  expo: {
    runtimeVersion: {
      policy: 'appVersion', // Match app version
    },
   

...(truncated)

</details>

## Source

[View on GitHub](https://github.com/TheBushidoCollective/han)