Lesson 17 of 26
Platform Differences: Platform.OS and Platform.select
How to branch behavior and styling by platform with Platform.OS and Platform.select, and how platform-specific file extensions let Metro pick the right file automatically.
You already know that React Native renders real native components instead
of HTML. The flip side of that is real too: iOS and Android are two
different operating systems with genuinely different conventions, and
sometimes your code needs to know which one it's running on. Platform.OS
and Platform.select are the two tools for that, and a platform-specific
file extension is a third, quieter way to do the same thing at the file
level.
Platform.OS: the simplest branch
Platform.OS is a plain string, read once, telling you which platform your
JS is currently running on:
import { Platform } from "react-native";
if (Platform.OS === "ios") {
console.log("Running on iOS");
} else if (Platform.OS === "android") {
console.log("Running on Android");
}It's exactly "ios" or "android" on a real device — nothing fancier, no
version number, no device model. If the app also targets React Native Web,
Platform.OS can additionally come back as "web", which matters if the
codebase genuinely spans phone and browser, though most React Native apps
only ever see the first two values.
Reach for a plain if/else on Platform.OS when the difference is a
single conditional value or a small behavioral branch: a status bar style,
a slightly different label, whether to show a control at all.
Platform.select: declarative branching for values and styles
Once a component is picking between more than one or two platform-specific
values — especially inside a style object — Platform.select reads better
than a chain of conditionals, because it describes "here's the whole set of
options for this one thing" in a single expression:
import { Platform, StyleSheet } from "react-native";
const styles = StyleSheet.create({
card: {
...Platform.select({
ios: {
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.15,
shadowRadius: 4,
},
android: {
elevation: 4,
},
default: {},
}),
},
});Platform.select takes an object keyed by platform name (plus an optional
default for anything not explicitly listed) and returns whichever value
matches the platform actually running. It works for entire style objects,
like above, or for a single value:
const headerHeight = Platform.select({ ios: 44, android: 56, default: 48 });The shadow example is a real, motivating case rather than a toy one: iOS
and Android genuinely use different mechanisms to draw the same visual
idea — a raised card with a soft shadow beneath it. iOS reads a handful of
shadow* props (color, offset, opacity, radius) on any view. Android
ignores all of those and instead reads a single elevation number,
deriving the shadow from Material Design's lighting model. Set only the
iOS props and Android renders a perfectly flat card; set only elevation
and iOS renders nothing at all. Platform.select is the tool for handing
each platform exactly the props it understands, in one expression.
Platform-specific file extensions
The third mechanism works at the file system level, not inside your code
at all. Name two files Button.ios.js and Button.android.js in the same
directory, and an ordinary import of ./Button resolves differently
depending on which platform Metro is building for:
// Button.ios.js
export default function Button() {
// an iOS-flavored button implementation
}
// Button.android.js
export default function Button() {
// an Android-flavored button implementation
}
// SomeScreen.js
import Button from "./Button"; // resolves to the matching file, per platformThis is a real, stable Metro convention — not a hack, not a workaround.
It's the right tool specifically when an entire component's implementation
genuinely differs by platform, rather than just a handful of style values
or props, since it lets you write two completely separate implementations
without a single Platform.OS check cluttering either one.
Choosing between the three
A rough rule of thumb: a single conditional value reads best as a plain
Platform.OS check; a small set of platform-specific values (especially
inside a style object) reads best as Platform.select; and an entire
component whose implementation diverges meaningfully by platform is the
signal to split it into .ios. / .android. files instead of littering
one file with branches. None of these are mutually exclusive — a real app
typically uses all three, at different scales, often in the same screen.
Two more real, long-standing platform differences worth knowing as motivation for reaching for any of this: Android's hardware or gesture back action has no direct iOS equivalent, which is why navigation libraries expose Android-specific hooks for intercepting it; and status bar, safe-area, and keyboard-avoiding behavior also differ enough between the platforms that "write it once, ignore the platform" quietly breaks on one of the two the moment an app gets non-trivial.
What to remember
Platform.OSis"ios"or"android"(or"web", if targeting React Native Web) — use it for simple conditionals.Platform.select({ ios, android, default })declaratively picks a value or whole style object per platform, and reads better than a chain of conditionals once there's more than one branch.Button.ios.js/Button.android.jslets Metro resolve a plain./Buttonimport to the right file automatically, per platform — a real bundler convention, not a hack.- iOS shadows (
shadow*props) and Android elevation are genuinely different mechanisms for the same visual effect — a concrete, common reason to reach forPlatform.select. - Android's back button/gesture has no iOS equivalent, which is why navigation libraries expose platform-aware handling specifically for it.
Check yourself
4 questions · pass 3/4 to unlock Permissions and Native Modules
1.Which value does
Platform.OSreturn when your code is running in an iOS app?2.A
Button.ios.jsand aButton.android.jsboth exist in the same folder, and elsewhere in the app there's a plainimport Button from "./Button". What decides which file actually gets used?3.What's the correct way to give a view an iOS shadow and an Android elevation without one platform silently ignoring the other's styling?
4.Why can't Android's hardware/gesture back button be handled by writing the exact same code you'd use for iOS's edge-swipe-back gesture?
4 left to answer