From expo
Implements over-the-air (OTA) updates in Expo React Native apps using expo-updates module. Covers app.json configuration, update checks, fetches, reloads, and runtime versions.
How this skill is triggered — by the user, by Claude, or both
Slash command
/expo:expo-updatesThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use this skill when implementing over-the-air (OTA) updates to deploy JavaScript and asset updates without app store releases.
Use this skill when implementing over-the-air (OTA) updates to deploy JavaScript and asset updates without app store releases.
// app.json
{
"expo": {
"updates": {
"enabled": true,
"checkAutomatically": "ON_LOAD",
"fallbackToCacheTimeout": 0,
"url": "https://u.expo.dev/[project-id]"
},
"runtimeVersion": {
"policy": "sdkVersion"
}
}
}
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>;
}
// app.config.ts
export default {
expo: {
runtimeVersion: {
policy: 'appVersion', // Match app version
},
// Or use custom logic
runtimeVersion: '1.0.0',
},
};
import * as Updates from 'expo-updates';
import { useEffect, useState } from 'react';
export function useUpdates() {
const [isChecking, setIsChecking] = useState(false);
const [isDownloading, setIsDownloading] = useState(false);
const [updateAvailable, setUpdateAvailable] = useState(false);
useEffect(() => {
const subscription = Updates.addListener((event) => {
if (event.type === Updates.UpdateEventType.UPDATE_AVAILABLE) {
setUpdateAvailable(true);
}
});
return () => subscription.remove();
}, []);
const checkForUpdate = async () => {
if (__DEV__) return;
setIsChecking(true);
try {
const update = await Updates.checkForUpdateAsync();
setUpdateAvailable(update.isAvailable);
} finally {
setIsChecking(false);
}
};
const downloadAndApplyUpdate = async () => {
if (__DEV__) return;
setIsDownloading(true);
try {
const update = await Updates.fetchUpdateAsync();
if (update.isNew) {
await Updates.reloadAsync();
}
} finally {
setIsDownloading(false);
}
};
return {
isChecking,
isDownloading,
updateAvailable,
checkForUpdate,
downloadAndApplyUpdate,
};
}
import { useEffect } from 'react';
import * as Updates from 'expo-updates';
export function useSilentUpdates() {
useEffect(() => {
async function update() {
if (__DEV__) return;
try {
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
// Don't reload immediately - wait for next app start
}
} catch (error) {
console.error('Update check failed:', error);
}
}
update();
}, []);
}
import * as Updates from 'expo-updates';
import { useState } from 'react';
import { View, Text, Button, ActivityIndicator } from 'react-native';
export function UpdateScreen() {
const [loading, setLoading] = useState(false);
const handleUpdate = async () => {
setLoading(true);
try {
const update = await Updates.fetchUpdateAsync();
if (update.isNew) {
await Updates.reloadAsync();
}
} catch (error) {
console.error('Update failed:', error);
} finally {
setLoading(false);
}
};
if (loading) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" />
<Text>Updating...</Text>
</View>
);
}
return (
<View>
<Text>Update Available</Text>
<Button title="Update Now" onPress={handleUpdate} />
</View>
);
}
// eas.json
{
"build": {
"production": {
"channel": "production"
},
"preview": {
"channel": "preview"
}
}
}
npx claudepluginhub thebushidocollective/han --plugin expoDeploys Expo apps to production via EAS Build, app store submission, and OTA updates. Use when publishing to iOS/Android or managing release channels.
Deploys Expo apps to production via app stores (iOS App Store, Google Play) and OTA updates. Guides builds, submissions, release channels, and optimization.
Expo and React Native patterns for mobile development with EAS Build, SDK integration, and native module configuration. Use when building Expo apps, configuring native features, or setting up app signing for store deployment.