React Native iPhone Duo checklist
Most React Native apps work on iPhone Duo because flexbox adapts. Whether yours does comes down to the patterns below, and each blocker is drawn further down the page.
- Works
- Works
- Breaks
- Breaks
- Works
- Breaks
- Works
- Breaks
- Breaks
- Looks dated
- Looks dated
- Unverified
- Unverified
- Needs native
Why most React Native apps survive the unfold
A React Native view tree is a native view tree. When iPhone Duo opens, UIKit resizes the root view and Yoga lays out everything inside it again, so a screen built from flex ratios reflows with no changes at all.
What changes is the range of widths you receive. The closed outer display is 466 points wide. The open inner display is 626 points wide held upright and 890 held sideways, and Split View can hand you anything in between. A single width check covers every one of those cases, as long as it reads the width live.
useWindowDimensions().width, at 600 points, gives one column when closed and two when open, in either orientation. It also drops back to one column when Split View narrows the window, with no extra code. Apple’s advice for native apps is to target size classes rather than breakpoints; this framework has no size classes, so a single width check is our closest equivalent.The blockers, drawn on iPhone Duo
Each figure shows the same screen twice on the inner display, at its real proportions. On the left is the pattern most apps ship today, and on the right is the same screen after the fix. Below each drawing is the code that produces it.
Built against an older SDK
This one is not a crash. An app built without the iOS 27 SDK runs at a familiar size and aspect ratio, with the rest of the display unused. The iOS 27.1 SDK is what lets it reach the screen edge, and several of the fixes below depend on it.
xcrun --sdk iphoneos --show-sdk-version
# 26.x → a familiar size and ratioxcrun --sdk iphoneos --show-sdk-version
# 27.1 → extends to the screen edgeDimensions.get cached at module scope
This is the most common React Native failure on a foldable. A width read when the module loads is captured once. Launch the app on the closed display, unfold it, and every style built from that number stays sized for 466 points on a 626-point screen. Dimensions.addEventListener does not help, because a StyleSheet.create object is never rebuilt.
const { width } =
Dimensions.get('window');
const styles = StyleSheet.create({
card: { width: width - 32 },
});function Card({ children }) {
const { width } =
useWindowDimensions();
return (
<View style={{ width: width - 32 }}>
{children}
</View>
);
}Portrait lock ignored on the inner display
Apple states the inner display does not honour supported interface orientations, so a lock in Info.plist or an orientation library stops working without any warning. Layouts that assumed height is always greater than width get laid out sideways. The fix is not a stronger lock but a layout chosen by width.
import Orientation from
'react-native-orientation-locker';
Orientation.lockToPortrait();
// A portrait-only Info.plist is
// ignored in exactly the same way.const { width } = useWindowDimensions();
return width >= 600
? <SidebarLayout />
: <StackLayout />;Symmetric safe-area maths
react-native-safe-area-context reports the real insets, and SafeAreaView applies them correctly. The bugs are in hand-written maths that collapses left and right into one value. That was harmless when both edges matched, but on iPhone Duo they differ, so one side is over-padded and the content drifts off-centre.
const insets = useSafeAreaInsets();
<View style={{
paddingHorizontal:
Math.max(insets.left, insets.right),
}} />const insets = useSafeAreaInsets();
<View style={{
paddingLeft: insets.left,
paddingRight: insets.right,
}} />Split View looks frozen
Every app takes part in Split View on iPhone Duo, with no opt-in. An app there can be visible without being active, so code that pauses video, timers or animation whenever the state leaves active stops content the user is still looking at.
AppState.addEventListener(
'change',
(s) => {
if (s !== 'active') {
player.pause();
}
},
);AppState.addEventListener(
'change',
(s) => {
if (s === 'background') {
player.pause();
}
},
);16:9 video on a 1.42 display
The inner display is far squarer than a phone. A full-screen 16:9 player fitted to the 890-point width leaves 125 points of black bars. Apple’s guidance is to use that space rather than fight it: keep the player at 16:9 and put related content beneath it.
<Video
style={StyleSheet.absoluteFill}
resizeMode="contain"
/><View style={{ flex: 1 }}>
<Video style={styles.player} />
<FlatList data={related} {...rest} />
</View>
// styles.player = { aspectRatio: 16/9 }What React Native cannot reach without native code
Correct layout is reachable from JavaScript, but awareness of the hinge is not. None of the following have JavaScript bindings today:
ReservedRegion, which describes where the hinge divides the displayArrangementView, for pose-aware primary and secondary placementonHingeChange, which reports the hinge angle- Scene accessories, for content on the outer display while the app runs on the inner one
- The vertical toolbar APIs
Without them, an app can lay out correctly at every size and still put a button on the fold.
<View style={{
flex: 1,
justifyContent: 'center',
}}>
<PrimaryButton />
</View>// Your own bridge around Swift's
// reservedRegions(kind: .division).
// No package ships this today.
const hinge = useHingeRegion();For feeds, commerce, messaging and media, correct layout is the whole job. For a canvas, an editor, a drawing app or a game board, budget for a native module that wraps reservedRegions(kind:) and passes the frames into JavaScript.
Find them in your codebase
Each of these searches maps to one of the blockers above. Every hit outside a component’s render function deserves a look.
grep -rn "Dimensions.get" src/
grep -rn "Math.max(insets" src/
grep -rnE "isPad|lockToPortrait|UISupportedInterfaceOrientations" src/ ios/
grep -rn "AppState" src/
Work through them in that order. The first four fixes are usually a day’s work and account for nearly all of the visible improvement.
Then check your dependencies
Your own code is half the problem. The other half is the forty libraries under
it, and they are not in the same state as each other: react-native-screens
shipped iPhone Duo fixes on 13 October, React Native’s own fold API is still an
open pull request, and npm install expo-hinge installs a placeholder rather
than Expo’s module.
We went through them one at a time in React Native libraries on iPhone Duo, with the version numbers and pull request states you can re-check yourself.
For the long-form version, with the reasoning behind each pattern and the full migration order, read what breaks in React Native on iPhone Duo, and what works. Building with Expo? Expo apps on iPhone Duo covers EAS build images, app.json and native tabs. Built the app with an AI tool? Start with why vibe-coded apps break on iPhone Duo.