skip to content

In React Native, how do Platform.isPad, Platform.isTV and Platform.isVision decide the device type, and what do they return on Android?

level: middleimportance: nice to knowfreq 25%

answer

  1. iOS reads constants.interfaceIdiom
  2. 'pad', 'tv', 'vision' idioms
  3. Android isTV reads uiMode
  4. Android isVision is always false
  5. isPad exists only on iOS

basics

~10 s

On iOS all three compare Platform.constants.interfaceIdiom with 'pad', 'tv' or 'vision'. On Android isTV checks uiMode === 'tv', isVision is always false, and isPad does not exist, so it reads as undefined.

solid answer

~30 s

On iOS, React Native derives all three from `Platform.constants.interfaceIdiom`, the device's interface idiom: `isPad` is `'pad'`, `isTV` is `'tv'`, `isVision` is `'vision'`. An iPad app running on Apple Vision Pro in its iPad-compatible mode reports `isVision` `false` and `isPad` `true`. On Android, `isTV` compares `Platform.constants.uiMode` with `'tv'`, `isVision` is hard-coded to `false`, and there is **no `isPad`**, so reading it gives `undefined`; React Native's TypeScript types only allow it after narrowing `Platform.OS` to `'ios'`. These flags describe the device, not the window, so an iPad in Split View is still `isPad`; size decisions belong to window dimensions.

code

typescript · 10 lines
typescript
import { Platform } from 'react-native';

// isPad exists only on the iOS member of the Platform union
export const isIPad = Platform.OS === 'ios' && Platform.isPad;
export const isTV = Platform.isTV;

// Android has no tablet flag: decide from the window width instead
export function isTabletWidth(windowWidth: number): boolean {
  return windowWidth >= 600;
}

go deeper

for a junior

Recall that isPad, isTV and isVision are booleans on Platform, and that isPad is an iOS-only property.

for a middle

Explain the sources: iOS interfaceIdiom for all three, Android uiMode for isTV, isVision always false on Android, and the Vision Pro iPad-compatibility case.

for a senior

Keep device flags for behaviour, such as TV focus navigation, and use window size for layout, centralizing both so tablets, foldables and Split View behave consistently.

for a principal

Decide which form factors the product supports and encode that as a small device capability module, rather than letting screens branch on raw Platform flags.

## Device-type flags on Platform Besides `Platform.OS`, React Native's `Platform` module exposes boolean getters for the kind of device: `isPad`, `isTV` and `isVision`. They are computed from `Platform.constants`, an object React Native reads once from a native module and caches. ## iOS: one field, three flags On iOS the native side reports the device's **interface idiom** as `Platform.constants.interfaceIdiom`, a string. The three flags are simple comparisons: 1. `Platform.isPad` is `interfaceIdiom === 'pad'`; 2. `Platform.isTV` is `interfaceIdiom === 'tv'`; 3. `Platform.isVision` is `interfaceIdiom === 'vision'`. React Native's docs call out one subtlety: an app running on Apple Vision Pro as an iPad app ("Designed for iPad") reports `isVision` as `false` and `isPad` as `true`, because the system presents it with the iPad idiom. Only an app running natively with the vision idiom sees `isVision` as `true`. ## Android: different sources, fewer flags Android has no interface idiom, so the flags work differently: - `Platform.isTV` compares `Platform.constants.uiMode` with `'tv'`. `uiMode` can be `'car'`, `'desk'`, `'normal'`, `'tv'`, `'watch'` or `'unknown'`. - `Platform.isVision` is always `false`. - `Platform.isPad` **does not exist**. Reading it at runtime yields `undefined`, which is falsy, so code that only tests truthiness still behaves, but the value is not a boolean. | Flag | iOS source | Android | |---|---|---| | `isPad` | `interfaceIdiom === 'pad'` | not defined, `undefined` | | `isTV` | `interfaceIdiom === 'tv'` | `uiMode === 'tv'` | | `isVision` | `interfaceIdiom === 'vision'` | always `false` | React Native's TypeScript types reflect this: `Platform` is a union discriminated by `OS`, and only the iOS member declares `isPad`. `Platform.isPad` therefore fails to type-check until you narrow with `Platform.OS === 'ios'`, which is the compiler telling you there is no Android tablet flag. ## What else is in Platform.constants `Platform.constants` is also the place for other device facts: - on both platforms: `reactNativeVersion` (`major`, `minor`, `patch`, optional `prerelease`) and `isTesting`; - on iOS: `osVersion`, `systemName`, `interfaceIdiom`, `forceTouchAvailable`, and `isMacCatalyst` where it applies; - on Android: `Version` (the API level), `Release`, `Model`, `Brand`, `Manufacturer`, `Fingerprint`, `uiMode` and a few more. `Platform.isTesting` returns the native flag only in development builds; in a release build it is always `false`. ## When the values are available All of these are synchronous getters. The first read of `Platform.constants` fetches the object from a native module and caches it; later reads, including `isPad`, `isTV` and `isVision`, are plain property lookups. They are available at module scope, before the first render, so it is safe to compute a module-level constant such as `const isIPad = Platform.OS === 'ios' && Platform.isPad`. They do not change while the process runs, which is exactly why they are unsuitable for anything that does change, such as the window size. ## A weather app example Suppose the weather app ships on phones, tablets and a TV build. The TV build needs focusable forecast tiles and larger type for viewing from across a room, which is a behaviour branch keyed on `Platform.isTV`. The tablet layout, a two-column forecast, is not a device question at all: it depends on how wide the window is right now, so it reads the window width and switches columns when an iPad enters Split View or an Android foldable opens. `Platform.isPad` is left for the rare iPad-only behaviour, such as a pointer hover affordance. ## Using the flags well - **Device type is not window size.** An iPad in Split View or Slide Over is still `isPad`, and a large Android tablet has no flag at all. For layout, such as showing a two-column forecast, read the current window dimensions instead of a device flag. - **Tablet detection on Android is a size question.** Since there is no `isPad`, teams decide from window width, which also handles foldables and resizable windows. - **TV and vision change interaction, not just size.** `isTV` is the signal to support focus-based navigation with a remote; `isVision` to adjust for a spatial display. Those are real behaviour branches, which is where the flags earn their keep. - **Keep the checks central.** A small `device.ts` that exports `isTablet(width)` and `isTV` stops `Platform.isPad` from being sprinkled through screens.

  • Why is Platform.isPad a poor signal for a two-column forecast layout in a React Native weather app?
    `isPad` reports the device idiom, not the space the app has. An iPad in Split View is still `isPad` with a narrow window, and Android tablets have no `isPad` at all. Base the column count on the current window width, which updates when the window resizes, and keep `isPad` for genuinely device-specific behaviour.
  • What does React Native's Platform.isTesting return in a release build?
    Always `false`. The getter only returns the native `isTesting` constant when `__DEV__` is true, so test-only branches keyed on it cannot activate in production builds.

saying these in an interview costs you the question

  • Platform.isPad returns true on Android tablets.
  • Platform.isVision is true for any iPad app running on Vision Pro.
  • Platform.isPad turns false when an iPad app is in Split View.
  • Platform.isTV only works on iOS.
  • Platform.isPad can be read without narrowing Platform.OS in TypeScript.