An Expo app is a React Native app, so everything in our React Native guide applies. But three Expo-specific choices decide what iPhone Duo actually shows: the image EAS builds your app on, what app.json asks for, and which tab bar Expo Router draws. This checklist covers all three, and each item links to the detail.
Expo iPhone Duo checklist
- Check which Xcode builds your app. EAS’s newest iOS image uses Xcode 26.6, so cloud builds today do not include the iOS 27 SDK.
- Do the layout work before the rebuild. The build that moves you to the new SDK is what exposes layout bugs, not the device.
- Change
"orientation": "portrait"to"default". Expo’s default template sets the lock, and the inner display ignores it. - Leave
ios.requireFullScreenoff. iPhone Duo honours the key but scales your app instead of exempting it. - Switch to
NativeTabsso iOS can move your tab bar into the side column. - Read size with
useWindowDimensions()instead of a module-scopeDimensions.get('window'). - Pad left and right separately with
react-native-safe-area-context; React Native’s ownSafeAreaViewis deprecated. - Stop choosing layouts with
Device.deviceType. iPhone Duo is a phone with a regular-width display. - Keep media playing in Split View by pausing on
background, notinactive. - Update eas-cli to 24.4.2 or later before building on Xcode 27.
- Test every pose in Device Hub with Xcode 27.1 beta, released 18 September.
- Treat App Store screenshots as optional for now. Apple lists iPhone Duo sizes, but uploads open later this year.
Expo has published no iPhone Duo guidance, so the Expo side of this is our analysis. Every Expo fact below links to Expo’s own documentation or code, checked on 15 September 2026.
Your build image decides what iPhone Duo shows
Apple defined three tiers of iPhone Duo behaviour, and which one your app gets depends only on the SDK it was built against:
| Built against | What your app gets on the inner display |
|---|---|
| Not the iOS 27 SDK | Runs at a familiar size and aspect ratio |
| iOS 27 SDK | Extends left of the status bar area |
| iOS 27.1 SDK | Reaches the screen edge; standard bar buttons move to the side |
For most Expo teams the SDK is not a choice made in Xcode. It comes from the EAS Build image. As of 15 September, Expo’s build infrastructure page lists no iOS image with Xcode 27. The latest and sdk-57 aliases point to macos-tahoe-26.5-xcode-26.6.
So an Expo app built on EAS today reaches iPhone Duo in the first tier. It runs and nothing crashes, but it does not use the display.
// eas.json: "latest" is Xcode 26.6
{
"build": {
"production": {
"ios": { "image": "latest" }
}
}
}# Uses the Xcode selected on your Mac
xcrun --sdk iphoneos --show-sdk-version
# 27.1 → build on this machine
eas build --platform ios --localTo see the full-screen result now, build on a Mac with Xcode 27.1 beta selected, using eas build --platform ios --local. Test with it, but do not submit from it: App Store builds need the release version of Xcode.
The break arrives with a rebuild
The first tier is safe. It is also temporary. Apple has announced that from April 2027, apps uploaded to App Store Connect must be built with the iOS 27 SDK or later. Expo aims to support stable Xcode releases on EAS, so newer images will follow.
The risky moment is the day your build moves to a newer image. The app claims more of the screen, and every assumption in the layout becomes visible at once, even though nobody changed a line of code. Do the layout work in this checklist first, so the rebuild is a non-event.
The portrait lock in app.json
Expo’s default project template sets "orientation": "portrait", so a new Expo app starts life with a portrait lock that nobody decided on. Apple’s Tech Talk is explicit that the inner display does not honour supported interface orientations. The lock is ignored there, and the app is laid out sideways when the device is held that way.
// app.json (default template)
{
"expo": {
"orientation": "portrait"
}
}// app.json
{
"expo": {
"orientation": "default"
}
}In Expo’s app config, orientation accepts default, portrait or landscape, and default means no lock. Pair it with a layout chosen by width, such as a two-column view at 600 points or wider.
requireFullScreen and orientation locks
ios.requireFullScreen maps to UIRequiresFullScreen, the key that opts an iPad app out of resizing. It is worth being precise about what it does here, because the answer is not the one most people expect.
Apple’s Tech Talk says iPhone Duo continues to honour the key — and then removes everything you would have set it for. Your app still resizes when someone opens or closes the device. Your supported orientations are respected, but the app scales on the inner display, including in Split View multitasking. Meanwhile the companion Tech Talk on multiple displays states plainly that all apps take part in multitasking on iPhone Duo.
So the flag does not buy an exemption; it buys a scaled presentation of an app that is still being resized. It sometimes gets turned on for an unrelated reason: the expo-screen-orientation docs note that locking orientation on iPad requires Requires Full Screen. Leave it off on iPhone Duo and build a layout that works at any width.
Native tabs get the system’s side column
On iPhone Duo, Apple’s guidelines move standard tab bars and toolbars into a column at the side on the closed outer display and on the inner display in landscape. That only happens to bars the system draws.
Expo Router’s Tabs wraps React Navigation’s JavaScript tab bar, so it stays at the bottom. NativeTabs is backed by UITabBarController on iOS. Import it from expo-router/unstable-native-tabs in SDK 54 to 57, and from expo-router/native-tabs from SDK 58. Expo’s default template on its main branch already uses it.
import { Tabs } from 'expo-router';
// Drawn by React Navigation in JS
export default function Layout() {
return <Tabs />;
}import { NativeTabs } from
'expo-router/unstable-native-tabs';
// UITabBarController on iOS
export default function Layout() {
return (
<NativeTabs>
<NativeTabs.Trigger name="home" />
</NativeTabs>
);
}We expect a UITabBarController-backed bar to get the same side placement as any native app built with the iOS 27.1 SDK. That is not yet verified on the device, so check it in Device Hub before you ship.
The React Native parts
Under Expo, the layout rules are React Native’s. Here is where common patterns in Expo projects stand:
- Looks dated
- Works
- Breaks
- Breaks
- Looks dated
- Unverified
- Works
- Breaks
- Works
- Looks dated
- Breaks
- Breaks
The React Native blockers are drawn in full, with before-and-after code, on the React Native iPhone Duo page. Two are worth singling out for Expo projects:
- Safe areas. React Native 0.81 deprecated its built-in
SafeAreaViewin favour ofreact-native-safe-area-context. Use that library, and applyinsets.leftandinsets.righton their own, never one value for both. - Split View. Code that pauses video or animation when
AppStateleavesactivewill freeze content that is still on screen.
expo-device and iPhone Duo
Device.deviceType reports a phone or a tablet, and iPhone Duo is a phone. A layout that serves its phone design for PHONE will stretch a single column across a 626-point, regular-width display. Branch on the window’s width instead.
Recognising the model by name is still in progress: an open pull request adds iPhone19,4 as “iPhone Duo” to expo-device’s model list.
Tooling on Xcode 27
Xcode 27 replaced the Simulator app with Device Hub. That broke eas build:dev, which looked for the old app by name. The issue was fixed in eas-cli 24.4.2, so update before you build locally on Xcode 27.
Xcode 27.1 beta, released on 18 September, adds the iPhone Duo simulator to Device Hub, and Apple’s Tech Talks describe controls to open, close, rotate and fold it. The simulator page covers its requirements and known issues.
Built your Expo app with an AI tool? The same checklist applies, with a few extra patterns covered in why vibe-coded apps break on iPhone Duo.