EAS Update for OTA updates. Use when setting up over-the-air updates.
/plugin marketplace add IvanTorresEdge/molcajete.ai/plugin install ivantorresedge-react-native-tech-stacks-js-react-native@IvanTorresEdge/molcajete.aiThis skill inherits all available tools. When active, it can use any tool Claude has access to.
This skill covers EAS Update for over-the-air (OTA) updates in React Native apps.
Use this skill when:
INSTANT UPDATES - Ship JavaScript changes without app store review.
✅ CAN update OTA:
- JavaScript code
- TypeScript code
- Assets (images, fonts)
- Styling changes
- New screens/components
- Bug fixes in JS
❌ CANNOT update OTA:
- Native code changes
- New native modules
- Native dependencies
- App permissions
- Build configuration
# Install expo-updates
npx expo install expo-updates
# Configure EAS Update
eas update:configure
// eas.json
{
"build": {
"preview": {
"channel": "preview"
},
"production": {
"channel": "production"
}
}
}
// app.config.ts
export default {
expo: {
// ...other config
updates: {
url: 'https://u.expo.dev/your-project-id',
fallbackToCacheTimeout: 0,
},
runtimeVersion: {
policy: 'appVersion',
},
},
};
// app.config.ts
// Option 1: Based on app version (recommended for most apps)
export default {
expo: {
runtimeVersion: {
policy: 'appVersion', // Uses version from app.json
},
},
};
// Option 2: Based on SDK version
export default {
expo: {
runtimeVersion: {
policy: 'sdkVersion',
},
},
};
// Option 3: Based on native code fingerprint
export default {
expo: {
runtimeVersion: {
policy: 'fingerprint',
},
},
};
// Option 4: Manual version
export default {
expo: {
runtimeVersion: '1.0.0',
},
};
# Publish to preview channel
eas update --channel preview --message "Fix login bug"
# Publish to production channel
eas update --channel production --message "Release 1.2.1 hotfix"
# Publish specific branch
eas update --branch main --message "Latest changes"
# Publish with auto-generated message
eas update --channel production --auto
# Create a new branch
eas branch:create staging
# Map channel to branch
eas channel:create staging
eas channel:edit staging --branch staging
# View channels
eas channel:list
# View branches
eas branch:list
# Roll back to previous update
eas channel:rollback production
import * as Updates from 'expo-updates';
// Check for updates on app start
async function checkForUpdates() {
try {
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
await Updates.reloadAsync();
}
} catch (error) {
console.error('Error checking for updates:', error);
}
}
// Use in app entry
useEffect(() => {
if (!__DEV__) {
checkForUpdates();
}
}, []);
import { useEffect, useState } from 'react';
import * as Updates from 'expo-updates';
interface UpdateState {
isChecking: boolean;
isAvailable: boolean;
isDownloading: boolean;
error: Error | null;
checkForUpdate: () => Promise<void>;
downloadAndRestart: () => Promise<void>;
}
export function useUpdates(): UpdateState {
const [isChecking, setIsChecking] = useState(false);
const [isAvailable, setIsAvailable] = useState(false);
const [isDownloading, setIsDownloading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const checkForUpdate = async () => {
if (__DEV__) return;
setIsChecking(true);
setError(null);
try {
const update = await Updates.checkForUpdateAsync();
setIsAvailable(update.isAvailable);
} catch (e) {
setError(e as Error);
} finally {
setIsChecking(false);
}
};
const downloadAndRestart = async () => {
if (!isAvailable) return;
setIsDownloading(true);
try {
await Updates.fetchUpdateAsync();
await Updates.reloadAsync();
} catch (e) {
setError(e as Error);
setIsDownloading(false);
}
};
// Check on mount
useEffect(() => {
checkForUpdate();
}, []);
return {
isChecking,
isAvailable,
isDownloading,
error,
checkForUpdate,
downloadAndRestart,
};
}
import { useUpdates } from '@/hooks/useUpdates';
export function UpdateBanner(): React.ReactElement | null {
const { isAvailable, isDownloading, downloadAndRestart } = useUpdates();
if (!isAvailable) return null;
return (
<View className="bg-blue-600 px-4 py-3 flex-row items-center justify-between">
<Text className="text-white">A new version is available</Text>
<TouchableOpacity
onPress={downloadAndRestart}
disabled={isDownloading}
className="bg-white px-4 py-2 rounded"
>
<Text className="text-blue-600 font-semibold">
{isDownloading ? 'Updating...' : 'Update Now'}
</Text>
</TouchableOpacity>
</View>
);
}
// app.config.ts
export default {
expo: {
updates: {
url: 'https://u.expo.dev/your-project-id',
checkAutomatically: 'ON_LOAD', // or 'ON_ERROR_RECOVERY', 'NEVER'
fallbackToCacheTimeout: 0, // 0 = use cache, don't wait
},
},
};
import * as Updates from 'expo-updates';
useEffect(() => {
const subscription = Updates.addListener((event) => {
if (event.type === Updates.UpdateEventType.UPDATE_AVAILABLE) {
// New update downloaded
Alert.alert(
'Update Available',
'A new version has been downloaded. Restart to apply?',
[
{ text: 'Later', style: 'cancel' },
{ text: 'Restart', onPress: () => Updates.reloadAsync() },
]
);
} else if (event.type === Updates.UpdateEventType.ERROR) {
// Update error
console.error('Update error:', event.message);
}
});
return () => subscription.remove();
}, []);
# .github/workflows/eas-update.yml
name: EAS Update
on:
push:
branches: [main, staging]
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Setup EAS
uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- name: Publish update
run: |
if [ "${{ github.ref }}" == "refs/heads/main" ]; then
eas update --channel production --auto --non-interactive
else
eas update --channel staging --auto --non-interactive
fi
# View update history
eas update:list --channel production
# Rollback to previous update
eas channel:rollback production
# Rollback to specific update
eas channel:edit production --branch <branch-name>
# Create experiment channels
eas channel:create production-control
eas channel:create production-variant
# Deploy different versions
eas update --channel production-control --message "Control version"
eas update --channel production-variant --message "Variant A"
# Route users to channels based on criteria
import * as Updates from 'expo-updates';
// Get current update info
console.log('Channel:', Updates.channel);
console.log('Runtime Version:', Updates.runtimeVersion);
console.log('Update ID:', Updates.updateId);
console.log('Created At:', Updates.createdAt);
console.log('Is Embedded Launch:', Updates.isEmbeddedLaunch);
# View update analytics
eas update:list --channel production --limit 10
# View update details
eas update:view <update-id>
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.