iPhone Duo · Expo and React Native
The iPhone Duo is Apple’s first foldable: a small cover screen outside and a large inner screen that folds down the middle. Your app moves between them as the device opens, closes and rotates. A React Native app does not need native code to handle that well. It needs the right build settings and a layout sized from the window.
What iOS expects from an app on the Duo
Apple’s Preparing your app for iPhone Duo starts from one idea: “Resizing is a key feature for your app on iPhone Duo.” Its checklist page turns that into rules that apply to JavaScript layouts as much as to UIKit:
- Size from the window, not the screen. The app’s size “changes whenever the device folds or unfolds, and it doesn’t always match the size of the screen.”
- Re-read the size when it changes. A fold can resize the app in the middle of a session, so a size read once at launch goes stale.
- Don’t decide layout by orientation. “The device’s orientation doesn’t tell you what shape your app is.” Compare width and height instead.
- Don’t decide layout by device type. An iPhone app can now have any combination of size classes, so a phone-or-tablet check is wrong on the Duo.
The documentation page adds one more rule: keep content out of the fold. iOS reports the fold as a reserved region, and Apple asks you to look for controls “that appear in the fold, and are difficult to see or interact with.” It also notes that on the Duo, navigation bars, toolbars and tab bars can move into a vertical rail on the side of the display. System navigation handles that for you. Hand-drawn bars do not.
Step 1: adopt the scene life cycle
A scene is one window of your app. Under UIKit’s scene life cycle (UIScene), a scene delegate, not your app delegate, creates that window and hears when it comes to the foreground or goes to the background. iOS 27 requires the scene life cycle for apps built with the iOS 27 SDK, and an app without it fails at launch.
- Expo SDK 58 (changelog) uses the scene life cycle by default. With Continuous Native Generation, upgrade and run
npx expo prebuild --clean. If you maintainios/by hand, apply the changes in Expo’s migration guide. - Expo SDK 57 can stay where it is. Expo backported scene support to it as the
ios.enableSceneSupportsetting ofexpo-build-properties(expo@57.0.23or newer; runnpx expo install --fixfirst if you are on an older 57 patch). Add it to your app config, then runnpx expo prebuild --clean. It works only with the standard AppDelegate template. Kitlist took this route for its Duo layout, with no native code. - React Native 0.88 (release notes) adds scene entry points. New apps from the template use them. An existing app has to move its entry point to a scene delegate itself.
Native modules can break under scenes, because some app delegate callbacks stop firing. Check your native dependencies’ release notes for scene support. UIScene life cycle is required: fix it for Xcode 27 has the steps for each stack, including the Expo SDK 57 setting and the React Native 0.88 migration.
Step 2: build with Xcode 27.1
How the Duo shows your app on its inner screen depends on the iOS SDK the build used:
| Built with | On the inner screen |
|---|---|
| An SDK before iOS 27.0 (Xcode 26.x) | Compatibility mode: iPhone-sized in the middle, black on either side |
| The iOS 27.0 SDK | A wide window, so width-based layouts show up, but the app is not told where the fold is |
| The iOS 27.1 SDK (Xcode 27.1) | The whole inner screen, with the fold available to lay out around |
The cover screen looks the same in all three cases. Xcode 27.1 needs macOS 26.6 or later (Apple) on an Apple silicon Mac (MacRumors). The apps it builds must target iOS 15.0 or later. If your Mac can’t run it, a VibeView cloud build can. Set "xcode": "27.1" for iOS in vibeview.json, or add --xcode 27.1 to one vibeview build --cloud run. See Build for iPhone Duo with Xcode 27.1.
Step 3: size the layout from the window
React Native’s useWindowDimensions() “automatically updates all of its values when screen size or font scale changes”. The 0.88 release notes add that under the scene life cycle the window can resize, and Dimensions.get() follows it too. In Kitlist, on Expo SDK 57 with scene support on, a component that reads the hook re-rendered on every fold, unfold and rotation.
Put the layout decision in one hook. It returns whether to show two panes and which way to arrange them:
import { useWindowDimensions } from 'react-native';
export function useLayout() {
const { width, height } = useWindowDimensions();
const shortSide = Math.min(width, height);
const longSide = Math.max(width, height);
const twoPanes = shortSide >= 600 && longSide >= 640;
return { twoPanes, direction: width > height ? 'row' : 'column' } as const;
}
These are the thresholds Kitlist uses. At about 466×678 points, the cover screen is under 600 on its short side, so it shows one pane. The inner screen, about 951×669 points, passes both tests and shows two: side by side when open, stacked when rotated. The same rule gives an iPad two panes and keeps a landscape iPhone on one, because nothing in it names a device.
A few habits keep it working:
- Don’t cache sizes. React Native’s Dimensions docs warn that values “may change (e.g due to device rotation, foldable devices etc)”. Read them during render, not into a
StyleSheetat import time. - Drop checks by device or orientation.
Platform.isPad, a list of known iPhone heights, or “landscape means tablet” all give wrong answers on the Duo. - Take the portrait lock off. Expo’s ScreenOrientation docs say that on iOS 27,
requireFullScreen, the app config key that used to opt an app out of resizing, no longer opts it out, andlockAsync“may have no effect”. Design for both shapes instead. - Keep state outside the layout. Keep the selected item in the route, for example
/trip/[id]. Then folding from two panes to one still shows it. Don’t remount the navigator when the layout switches, or you lose the tab, scroll position and form input.
Step 4: apply safe areas per edge
Safe areas matter more on the Duo. Expo’s SDK 58 notes warn that horizontal insets may mean adding <SafeAreaView> in more places, such as inside scroll views, and recommend updating react-native-safe-area-context with npx expo install react-native-safe-area-context.
The cover screen shows its status bar only at the top, but its safe area still reserves an 84-point strip down the whole right edge. Two patterns follow from that:
- Pad only the edges a view touches.
SafeAreaViewtakes anedgesprop (top,right,bottom,left). A pane on the left of the inner screen does not need the right inset. - Add insets to absolutely positioned views yourself. A floating button ignores its parent’s padding. Kitlist’s round
+button sat inside that strip until Kitlist added the inset:right: 20 + insets.right, withinsetsfromuseSafeAreaInsets().
Step 5: handle the fold
The fold runs down the middle of the inner screen, about 40 points wide. You have three options, from least to most work.
Split at half the window. Because the fold is centered on the inner screen in both orientations, a 50/50 split with a gutter of about 40 points keeps content off it. This needs no native code. It is what Kitlist ships, and its two panes stay clear of the fold open, partly folded and rotated.

Use the system arrangement. Apple’s ArrangementView places a primary and a secondary view around the fold for you. Expo SDK 58 exposes it as ArrangementView in @expo/ui/swift-ui, which needs iOS 27.1 or later. On earlier iOS versions it shows its children without arranging them around the fold.
Read the fold’s frame. UIKit reports the fold as a reserved region with a frame and an active flag: active when partly folded, inactive when flat. React Native has no API for it, so this takes a small native module. Our Atlas sample wrote one and splits its panes exactly at the fold.
Posture is not layout. Expo SDK 58 adds
expo-hinge: useHinge() reports the hinge
angle and a status of closed, partiallyOpen, fullyOpen or unknown. On iOS it needs iOS
27.1 and a build made with Xcode 27.1. Expo’s docs are explicit: “Use hinge state for interactions
and effects, not for layout.” Lay out from the window; use the hinge for things like a tabletop
video control.
Step 6: test every posture
A foldable gives you more states to check than a phone. Walk through each of these and look for clipped content, controls in the fold, lost state and sheets that span both halves:
- Closed, on the cover screen, including with the keyboard up.
- Open and flat, on the inner screen.
- Partly folded, around 110 degrees and around 75 degrees.
- Open, rotated a quarter turn, where two panes stack.
- Folded closed again from each of those, with something selected and with nothing selected.
The cover screen is short. A sheet whose save button sits inside its scroll view can end up under the keyboard there, as Kitlist’s did. Put actions in a footer outside the scroll view.
On a Mac: Xcode 27.1 Device Hub
The iPhone Duo simulator ships with Xcode 27.1. Device Hub’s toolbar has fold buttons, and holding Option shows a slider for the exact angle (SwiftLee). If the Duo is missing or won’t fold, How to get the iPhone Duo simulator in Xcode 27.1 covers the runtime update and the right Device Hub.
In a browser: VibeView’s iPhone Duo
You can also fold the Duo from a browser on Windows, Linux or any Mac: VibeView hosts Apple’s simulator and streams the session to you. What you see there is the simulator, so screen sizes, postures and rotation match the hardware, while the hinge’s physical feel, the sensors and real-world speed do not.
- Open Devices and choose iPhone Duo (iOS 27.1), marked Foldable.
- Choose your app’s build: an iOS simulator build, from a cloud build or an upload. For the full inner screen, build it with Xcode 27.1 as in Step 2.
- Start the session. The device boots folded shut, so your app opens on the cover screen first.
- In the Device tab, pick Closed, Partial or Open, or turn on Set by angle and choose any angle from 0 to 180 degrees. Rotate works in every posture.
To work on the layout live, run your React Native app on the Duo with Fast Refresh: vibeview dev --platform ios --model "iPhone Duo" connects a debug build to your local Metro. See Live Development.
A coding agent can run the same checks. The CLI and MCP server give it set-posture closed|partial|open, set-posture --angle, rotate, the UI tree and screenshots. Each set-posture response names the posture the device settled in and which screen is lit, so the agent can check the state the device actually reached. Set the iPhone Duo hinge angle from the command line has the commands, and the iPhone Duo simulator page explains how to share a running session.
Frequently asked questions
Do I need to upgrade to Expo SDK 58 for the iPhone Duo?
No. Expo SDK 57 has a backported setting for the scene life cycle, and a layout sized from the window does the rest. Upgrade to SDK 58 when you want its extras: the expo-hinge package, Expo UI’s ArrangementView, and Expo packages that measure each window rather than the screen.
Does React Native have a fold or posture API?
React Native itself does not. The useWindowDimensions hook updates on every fold and rotation, and that is enough for layout. Expo SDK 58’s expo-hinge package reports the hinge angle and posture. For the fold’s exact position you need a small native module.
Why is my app small in the middle of the Duo's inner screen?
Your build used an iOS SDK older than 27.0, and the Duo shows apps like that in compatibility mode: phone-sized, with black on both sides. Adopt the scene life cycle, rebuild with Xcode 27.1, and the app fills the inner screen.
Can I test an Expo app on the iPhone Duo without a Mac?
Yes. Build it with a VibeView cloud build on Xcode 27.1, then open it on VibeView’s iPhone Duo in your browser and fold it from the Device tab.
Published 29 October 2026 · Expo, React Native and Apple documentation checked 29 October 2026
Related guides
- An AI agent adds iPhone Duo support to an Expo app
- An AI agent makes Atlas a first-class iPhone Duo app
- UIScene life cycle is required: fix it for Xcode 27
- Build for iPhone Duo with Xcode 27.1, no Mac needed
- How to get the iPhone Duo simulator in Xcode 27.1
- Set the iPhone Duo hinge angle from CLI, CI or an agent
In the documentation
Try it yourself
Open a live iOS simulator, Android emulator, Apple TV, Android TV or Roku in your browser.
Start free