In-App Updates
App Integration
Wire update checks into a React Native screen.
This example shows a complete React component that loads update status, starts immediate or flexible update flows, and falls back to the store page when needed.
InAppUpdatePanel
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Button, Text, View } from 'react-native';
import {
addInstallStateListener,
canOpenStorePage,
canStartFlexibleUpdate,
canStartImmediateUpdate,
completeFlexibleUpdate,
getUpdateStatus,
InAppUpdatesError,
openStorePage,
startFlexibleUpdate,
startImmediateUpdate,
} from '@rnforge/react-native-in-app-updates';
import type { InstallStateSubscription, UpdateStatus } from '@rnforge/react-native-in-app-updates';
const storeOptions = {
ios: {
appStoreId: '1234567890',
country: 'us',
},
};
export function InAppUpdatePanel() {
const [status, setStatus] = useState<UpdateStatus | null>(null);
const [loading, setLoading] = useState(false);
const [message, setMessage] = useState<string | null>(null);
const subscriptionRef = useRef<InstallStateSubscription | null>(null);
const loadStatus = useCallback(async () => {
setLoading(true);
setMessage(null);
try {
const next = await getUpdateStatus(storeOptions);
setStatus(next);
} catch (error) {
if (error instanceof InAppUpdatesError) {
setMessage(error.message);
} else {
setMessage('Unable to check for updates.');
}
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
void loadStatus();
return () => {
subscriptionRef.current?.remove();
subscriptionRef.current = null;
};
}, [loadStatus]);
const startImmediate = useCallback(async () => {
if (!status || !canStartImmediateUpdate(status)) return;
setLoading(true);
setMessage(null);
try {
const next = await startImmediateUpdate();
setStatus(next);
} catch (error) {
if (error instanceof InAppUpdatesError) {
setMessage(error.message);
} else {
setMessage('Unable to start the update.');
}
} finally {
setLoading(false);
}
}, [status]);
const startFlexible = useCallback(async () => {
if (!status || !canStartFlexibleUpdate(status)) return;
setLoading(true);
setMessage(null);
subscriptionRef.current?.remove();
subscriptionRef.current = null;
subscriptionRef.current = addInstallStateListener((event) => {
if (event.installStatus === 'downloaded') {
void completeFlexibleUpdate()
.then((next) => {
setStatus(next);
subscriptionRef.current?.remove();
subscriptionRef.current = null;
})
.catch((error) => {
if (error instanceof InAppUpdatesError) {
setMessage(error.message);
} else {
setMessage('Unable to complete the update.');
}
});
}
});
try {
const next = await startFlexibleUpdate();
setStatus(next);
} catch (error) {
subscriptionRef.current?.remove();
subscriptionRef.current = null;
if (error instanceof InAppUpdatesError) {
setMessage(error.message);
} else {
setMessage('Unable to start the download.');
}
} finally {
setLoading(false);
}
}, [status]);
const openStore = useCallback(async () => {
if (!status || !canOpenStorePage(status)) return;
setLoading(true);
setMessage(null);
try {
await openStorePage(storeOptions);
} catch (error) {
if (error instanceof InAppUpdatesError) {
setMessage(error.message);
} else {
setMessage('Unable to open the store.');
}
} finally {
setLoading(false);
}
}, [status]);
return (
<View>
<Text>Platform: {status?.platform ?? '...'}</Text>
<Text>Supported: {status == null ? '...' : status.supported ? 'yes' : 'no'}</Text>
<Text>
Update available: {status == null ? '...' : status.updateAvailable == null ? 'unknown' : status.updateAvailable ? 'yes' : 'no'}
</Text>
{status?.reason ? <Text>Reason: {status.reason}</Text> : null}
{message ? <Text>{message}</Text> : null}
<Button title="Check again" onPress={loadStatus} disabled={loading} />
{status && canStartImmediateUpdate(status) ? (
<Button title="Update now" onPress={startImmediate} disabled={loading} />
) : null}
{status && canStartFlexibleUpdate(status) ? (
<Button title="Download update" onPress={startFlexible} disabled={loading} />
) : null}
{status && canOpenStorePage(status) ? (
<Button title="Open store" onPress={openStore} disabled={loading} />
) : null}
</View>
);
}Notes
- Check status before starting a flow. Use helper functions like
canStartImmediateUpdateandcanStartFlexibleUpdateto guard each action. - Register the install-state listener before calling
startFlexibleUpdate()so you receive events from the start. - Clean up the listener on unmount to avoid stale callbacks.
- Complete a flexible update when the listener reports
event.installStatus === 'downloaded'. If you refresh status separately and show a manual Complete update button, guard it withcanCompleteFlexibleUpdate(status). - Use the store page fallback when an in-app update flow is not available.