Phone-only assumptions are finally expiring. Android 16 ignores screenOrientation, resizableActivity and aspect-ratio locks on displays with a smallest width at or above 600dp, so an app that insisted on portrait now gets handed a landscape tablet window whether it is ready or not. iPadOS keeps widening the range of window sizes an app has to survive, and foldables change geometry mid-session while your JS keeps running.
For a React Native codebase that has only ever rendered on a 390pt-wide phone, this is less a design problem than a layout-assumption audit. Here is the approach we use on client apps.
1. Find the hard-coded phone assumptions first
Before designing anything, grep for the patterns that break on a big or resizing window:
Dimensions.get('window')captured once at module scope. On a foldable or a resized iPad window, that value is stale the moment the app opens. Replace it with theuseWindowDimensionshook, which re-renders on change.- Fixed pixel widths on containers (
width: 350) instead offlex,maxWidth, or percentage widths. Platform.isPador a one-timeisTabletboolean computed at startup. Device class is not the same thing as current window size; a phone app in split view on a tablet is a narrow window on a large device.- Orientation locks in
app.json/Info.plist/ the Android manifest that you have been relying on to avoid landscape work. - Absolute positioning tuned to one screen height, and
ScrollViewcontent that assumes a single column.
That grep usually produces the real backlog. Everything after this is design.
2. Breakpoints over device checks
Drive layout from the current window width, not from the device:
import { useWindowDimensions } from 'react-native';
export function useBreakpoint() {
const { width } = useWindowDimensions();
if (width >= 1024) return 'expanded';
if (width >= 600) return 'medium';
return 'compact';
}
Three buckets — compact, medium, expanded — map cleanly onto Android's window size classes and onto the iPad's split-view widths, and they are few enough that QA can actually cover them. If you are on NativeWind 4 or Unistyles 3, use their breakpoint support instead of rolling your own; both re-evaluate on resize, which is the part hand-written logic usually gets wrong.
The rule we hold teams to: no component asks "am I on a tablet?" It asks "how much width do I have?" That keeps the same code correct in split view, in a floating window, and on a half-open foldable.
3. The two-pane pattern is most of the win
The majority of tablet complaints come down to a list-detail screen rendering a 900pt-wide row of text. The fix is a two-pane layout: list on the left, detail on the right, with the detail route still being a real route.
With Expo Router or React Navigation, keep one navigation state and change only the presentation:
function InboxScreen() {
const bp = useBreakpoint();
const [selectedId, setSelectedId] = useState(null);
if (bp === 'compact') {
return <MessageList onSelect={(id) => router.push(`/messages/${id}`)} />;
}
return (
<View style={{ flex: 1, flexDirection: 'row' }}>
<View style={{ width: 360 }}>
<MessageList onSelect={setSelectedId} selectedId={selectedId} />
</View>
<View style={{ flex: 1 }}>
{selectedId ? <MessageDetail id={selectedId} /> : <EmptyDetail />}
</View>
</View>
);
}
Two things to get right. First, deep links must still resolve on both layouts: /messages/123 should open the detail pane with the list beside it on expanded, and a pushed screen on compact. Second, if the window shrinks while a detail pane is open (split view, fold closing), collapse to the pushed-screen form without losing the selection — derive the visible layout from state, never mutate navigation history on resize.
Other layouts worth having an expanded variant for: modals that should become centered dialogs with a maxWidth rather than full-bleed sheets; grids that go from one column to three via a computed numColumns on your list; and forms, which should cap line length around 600–700pt instead of stretching an input across a 1280pt window.
4. Foldables and resize: the mid-session cases
Foldables are the stress test because the window changes while your app is alive. Three habits cover most of it:
- Make every layout a pure function of current dimensions and state, so a resize is just a re-render.
- Preserve state across configuration changes. On Android, the system may recreate the activity; make sure scroll position, form input and media playback position survive. Test by toggling the fold and by rotating with "don't keep activities" enabled in developer options.
- Pause expensive work on resize storms. A drag-resize on an iPad fires many layout passes; debounce anything costly keyed on width, and keep heavy animations on Reanimated's UI thread so a resize does not drop frames.
Also check camera and media screens specifically. Preview aspect ratios, crop overlays and fullscreen video are where hard-coded dimensions hide, and they look obviously wrong on a wide window.
5. Input: large screens are not only touch
Tablet users bring keyboards, trackpads and styluses, and Android desktop windowing is making this more common.
- Support keyboard navigation on primary flows. Focus order, Enter to submit, Escape to dismiss a modal.
- Give interactive elements a visible focus/hover state.
Pressableexposeshoveredandfocusedin its style callback, which is enough for most components. - Honor external keyboard shortcuts where it is cheap to do so, and keep tap targets at 44pt even when a pointer is attached.
None of this is required for a passing build, but it is the difference between "runs on a tablet" and "usable on a tablet".
6. Make it stick in CI and QA
Adaptive layout rots fast unless it is tested. What we put in place:
- A device matrix in the test plan: phone portrait, phone landscape, tablet portrait, tablet landscape, tablet split view at roughly one third width, and one foldable in both postures. Add iPad Stage Manager if the app is iPad-advertised.
- Maestro flows run against at least one large-screen emulator, not only a phone, so a broken two-pane route fails the build.
- Screenshot tests at the three breakpoint widths for the handful of screens that matter most — usually home, list-detail, checkout and settings.
- Store assets: both stores surface large-screen screenshots, and Google Play factors large-screen quality into tablet ranking. Ship real tablet screenshots, not upscaled phone ones.
What this costs
On a typical mid-size app, the audit in step 1 takes a day or two, the breakpoint plumbing another day, and then it is a screen-by-screen grind — usually one to three days per complex screen and far less for simple ones. The work is highly parallelizable and it does not require a React Native upgrade, which makes it a good candidate to run alongside other roadmap work.
The forcing function is real, though: once a platform stops honoring your orientation lock, "we only support phones" stops being a supported configuration and starts being a bug report.
If you want a second pair of eyes on a large-screen audit, or you need senior React Native help to work through the screen backlog, get in touch.