Código Específico de Plataforma
Platform.OS, Platform.select y extensiones de archivo .ios/.android.
Busca en todas las páginas de la documentación
Platform.OS, Platform.select y extensiones de archivo .ios/.android.
Tarjeta de referencia rápida - lista para copiar y pegar.
import { Platform, StyleSheet, Text, View } from "react-native";
const HEADER_HEIGHT = Platform.select({ ios: 44, android: 56, default: 48 });
export function ScreenHeader({ title }: { title: string }) {
return (
<View style={styles.header}>
<Text style={styles.title}>{title}</Text>
</View>
);
}
const styles = StyleSheet.create({
header: {
height: HEADER_HEIGHT,
paddingTop: Platform.OS === "ios" ? 4 : 0,
backgroundColor: Platform.select({ ios: "#f8f8f8", android: "#6200ee" }),
justifyContent: "center",
paddingHorizontal: 16,
},
title: {
fontSize: 17,
fontWeight: Platform.select({ ios: "600", android: "500" }),
color: Platform.OS === "android" ? "#fff" : "#000",
},
});Cuándo usarlo: Pequeñas diferencias de estilo o constante entre iOS y Android en un archivo de componente compartido.
// AppButton.tsx - entrada compartida; Metro resuelve archivos de plataforma automáticamente
import { AppButton } from "./components/AppButton";
export default function CheckoutScreen() {
return (
<AppButton
label="Pay now"
onPress={() => console.log("checkout")}
/>
);
}// components/AppButton.tsx - API compartida, re-exporta implementación de plataforma
export { AppButton } from "./AppButton.native";
export type { AppButtonProps } from "./AppButton.types";// components/AppButton.types.ts
export interface AppButtonProps {
label: string;
onPress: () => void;
disabled?: boolean;
}// components/AppButton.native.tsx - fallback para plataformas que no se dividieron
import { Platform } from "react-native";
import { AppButtonIos } from "./AppButton.ios";
import { AppButtonAndroid } from "./AppButton.android";
export function AppButton(props: import("./AppButton.types").AppButtonProps) {
return Platform.OS === "android" ? (
<AppButtonAndroid {...props} />
) : (
<AppButtonIos {...props} />
);
}// components/AppButton.ios.tsx
import { Pressable, StyleSheet, Text } from "react-native";
import type { AppButtonProps } from "./AppButton.types";
export function AppButtonIos({ label, onPress, disabled }: AppButtonProps) {
return (
<Pressable
onPress={onPress}
disabled={disabled}
style={({ pressed }) => [
styles.button,
pressed && styles.pressed,
disabled && styles.disabled,
]}
>
<Text style={styles.label}>{label}</Text>
</Pressable>
);
}
const styles = StyleSheet.create({
button: {
backgroundColor: "#007aff",
borderRadius: 10,
minHeight: 44,
alignItems: "center",
justifyContent: "center",
paddingHorizontal: 20,
},
pressed: { opacity: 0.7 },
disabled: { opacity: 0.4 },
label: { color: "#fff", fontSize: 17, fontWeight: "600" },
});// components/AppButton.android.tsx
import { Pressable, StyleSheet, Text } from "react-native";
import type { AppButtonProps } from "./AppButton.types";
export function AppButtonAndroid({ label, onPress, disabled }: AppButtonProps) {
return (
<Pressable
onPress={onPress}
disabled={disabled}
android_ripple={{ color: "rgba(255,255,255,0.3)" }}
style={[styles.button, disabled && styles.disabled]}
>
<Text style={styles.label}>{label}</Text>
</Pressable>
);
}
const styles = StyleSheet.create({
button: {
backgroundColor: "#6750a4",
borderRadius: 4,
minHeight: 48,
alignItems: "center",
justifyContent: "center",
paddingHorizontal: 24,
elevation: 2,
},
disabled: { opacity: 0.4 },
label: { color: "#fff", fontSize: 15, fontWeight: "500", textTransform: "uppercase" },
});// utils/share.ts - módulo compartido con Platform.select para comportamiento
import { Platform, Share } from "react-native";
export async function shareUrl(url: string, message: string) {
if (Platform.OS === "web") {
if (navigator.share) {
await navigator.share({ title: message, url });
return;
}
await navigator.clipboard.writeText(url);
return;
}
await Share.share(
Platform.select({
ios: { url, message },
android: { message: `${message}\n${url}` },
default: { message: `${message} ${url}` },
})!
);
}Lo que esto demuestra:
Platform.select para constantes y diferencias de carga útil de Share.share en un archivo.ios.tsx / .android.tsx para diferencias de chrome de botón materialmente diferentesAppButton.types.ts compartido manteniendo un contrato de props público único.ios.js / .android.js / .native.js / .jsPlatform.OS === "web" para objetivos web de Expo sin un archivo web separado.ios.tsx - .native.tsx - .tsx (en simulador/dispositivo de iOS).Platform.OS es una cadena de tiempo de ejecución leída en el subproceso de JS - segura dentro de render y manejadores.Platform.select devuelve la clave de plataforma coincidente o default - puede devolver objetos, números o funciones..ios.tsx.Platform.Version con detección de características.| Importación | El bundle de iOS carga | El bundle de Android carga |
|---|---|---|
./Foo | Foo.ios.tsx - Foo.native.tsx - Foo.tsx | Foo.android.tsx - Foo.native.tsx - Foo.tsx |
./Foo.ios | Solo archivo de iOS explícito | N/A a menos que se importe |
.native.tsx se ejecuta en iOS y Android pero no en web cuando existe un hermano .web.tsx..web.tsx se elige para exportaciones web de Expo.// Forma de objeto - elige un valor
const font = Platform.select({ ios: "System", android: "Roboto", default: "sans-serif" });
// Forma de función - perezoso por plataforma
const createStyles = Platform.select({
ios: () => StyleSheet.create({ card: { shadowOpacity: 0.2 } }),
android: () => StyleSheet.create({ card: { elevation: 4 } }),
});
// Ternario en línea - bien para propiedades puntuales
paddingTop: Platform.OS === "ios" ? 8 : 0,| Acercamiento | Mejor para | Evita cuando |
|---|---|---|
Platform.select en StyleSheet | Colores, alturas, pesos de fuente | Los componentes completos difieren |
Platform.OS si/sino | Lógica condicional, devoluciones tempranas | Muchos bloques JSX duplicados |
Archivos .ios / .android | Wrappers grandes de UI o módulos nativos | Un cambio de color de una línea |
*.web.tsx | APIs DOM específicas de web de Expo | Aplicaciones solo móviles sin objetivo web |
import { Platform } from "react-native";
const iosVersion =
Platform.OS === "ios" ? parseInt(String(Platform.Version), 10) : 0;
const androidApi =
Platform.OS === "android" ? Platform.Version : 0;
const supportsBlur = iosVersion >= 13 || androidApi >= 31;Platform.Version en iOS es una cadena como "17.0" - analiza antes de comparar numérica.34).import { Platform, type PlatformOSType } from "react-native";
type MobileOS = Extract<PlatformOSType, "ios" | "android">;
function isMobile(os: PlatformOSType): os is MobileOS {
return os === "ios" || os === "android";
}
// El retorno de Platform.select puede ser indefinido - proporciona default
const height = Platform.select({ ios: 44, android: 56 }) ?? 48;
// Comparte módulos específicos de plataforma con exportaciones idénticas
export type { AppButtonProps } from "./AppButton.types";PlatformOSType incluye "web" en Expo - estrecha antes de usar APIs solo móviles.Platform.select sin default puede devolver undefined - usa ?? fallback..types.ts importado por todas las variantes de plataforma.Dividir archivos demasiado pronto - Tres diferencias de color de una línea no necesitan archivos .ios y .android. Solución: Usa Platform.select hasta que JSX diverja materialmente.
Importar archivos .ios directamente - import X from "./Foo.ios" rompe bundles de Android o envía el módulo incorrecto. Solución: Importa ./Foo siempre y deja que el bundler elija el archivo correcto.
Olvidar default en Platform.select - Los objetivos web o macos obtienen undefined. Solución: Siempre agrega default para aplicaciones multi-plataforma de Expo.
Controles de plataforma en estilos compartidos fuera de create - Llamar a Platform.select una vez en el alcance del módulo está bien; los valores dinámicos por tema pertenecen dentro de hooks.
Lógica empresarial duplicada en archivos de plataforma - La copia y pegue diverge con el tiempo. Solución: Comparte lógica en utils/*.ts; divide solo el límite vista/nativa.
Asumiendo Android === Material, iOS === Human Interface - Los usuarios esperan consistencia de marca. Solución: Unifica espacios y semántica; varía solo chrome donde las convenciones del SO lo exigen.
Comparar Platform.Version como cadena en iOS - "17" < "9" falla lexicográficamente. Solución: parseInt(String(Platform.Version), 10) antes de comparar numérica.
| Alternativa | Usa Cuando | No Usar Cuando |
|---|---|---|
Platform.select | Deltas de estilo/constante pequeños | Los componentes completos difieren |
Archivos .ios / .android | UI grande o importaciones nativas por SO | Cambio de color o altura de una línea |
expo-device / Constants | Modelo de dispositivo, isTablet, runtimeVersion | La enumeración SO simple es suficiente |
| Módulos nativos condicionales | La característica requiere TurboModule solo en un SO | Diferencia de estilo solo JS |
| Diseño universal | La marca exige paridad de píxeles | El HIG del SO afecta materialmente UX (hojas de compartir) |
react-native-unistyles breakpoints | Tokens de tema por plataforma en sistema de diseño | Altura de encabezado puntual |
"ios" y "android"."web", y RN soporta "macos" y "windows" en algunas plantillas.Platform.OS === "ios" en lugar de asumir solo dos plataformas.Foo.ios.tsx primero, luego Foo.native.tsx, luego Foo.tsx.Foo.android.tsx reemplaza el paso .ios.Platform.select para valores de estilo, constantes numéricas y objetos de configuración pequeños.*.web.tsx..ios) vence a .native vence a genérica .tsx.default, las plataformas no coincidentes reciben undefined.default o una clave explícita de web.Platform.select({...}) ?? fallback.app/ pero los componentes pueden dividirse normalmente..ios a menos que pretendas pantallas específicas del SO.const spacing = Platform.select({
ios: 8,
android: 12,
default: 10,
}); // number | undefined sin clave default - usa clave default"18.0") - analiza la versión principal entera para comparaciones.34) - compara numéricamente.{Platform.OS === "ios" && <BlurView />}) están bien.*.web.tsx cuando están presentes..web, web puede volver a .tsx o .native.tsx dependiendo de la configuración de resolución.*.web.tsx cuando el código móvil importa módulos solo nativos../Button siempre - deja que el bundler elija el archivo correcto.Component.types.ts con interfaces de props.Component.ios.tsx / Component.android.tsx importa los mismos tipos.Component.tsx barril único que re-exporta la implementación resuelta.Platform.OS a través de jest.spyOn(Platform, "OS", "get").Platform.OS es "web".ios - establécelo explícitamente por suite.expo-constants expone executionEnvironment, sessionId, sutilezas de platform y metadatos de EAS.Platform para ramificaciones rápidas del SO en componentes de UI..ios.ts / .android.ts con firmas de TypeScript coincidentes.index.ts para que el código de la aplicación permanezca independiente de la plataforma.Versiones de Stack: Esta página fue escrita para React 19.2.3, React Native 0.86.0 y Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última actualización: 16 jul 2026