Skip to content

t1k:rn:base:platform-specific

FieldValue
Modulebase
Version1.7.4
Effortmedium
Tools

Keywords: android, ios, native bridge, platform, Platform.select, react native

/t1k:rn:base:platform-specific

import { Platform, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
shadow: Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 4,
},
android: {
elevation: 4,
},
}),
});
// Conditional rendering
const StatusBarHeight = Platform.OS === 'ios' ? 44 : 24;
components/
├── HapticTab.tsx # shared logic
├── HapticTab.ios.tsx # iOS override (auto-resolved)
└── HapticTab.android.tsx # Android override (auto-resolved)

Metro resolves .ios.tsx.android.tsx.tsx automatically. No import changes needed.

Native Module Bridging (Legacy Architecture)

Section titled “Native Module Bridging (Legacy Architecture)”
modules/Haptics/index.ts
import { NativeModules, Platform } from 'react-native';
const { HapticsModule } = NativeModules;
export const triggerHaptic = (type: 'light' | 'medium' | 'heavy') => {
if (Platform.OS === 'ios') {
HapticsModule?.trigger(type);
}
};

Expo Modules API (Preferred for new native code)

Section titled “Expo Modules API (Preferred for new native code)”
modules/my-module/src/index.ts
// Use expo-modules-core for new native modules
import MyModule from './MyModuleModule';
export { MyModule };
import { useSafeAreaInsets } from 'react-native-safe-area-context';
export function Screen({ children }: { children: React.ReactNode }) {
const insets = useSafeAreaInsets();
return (
<View style={{ flex: 1, paddingTop: insets.top, paddingBottom: insets.bottom }}>
{children}
</View>
);
}
  • Never hardcode status bar heights — use useSafeAreaInsets()
  • Use platform extensions for substantial platform differences, Platform.select for style-only
  • Expo Modules API > Legacy NativeModules for new native code
  • Test on both iOS simulator AND Android emulator — behavior often differs
  • Platform.select returns undefined if platform not listed — always provide default key
  • Platform extensions work in Metro but NOT in Jest — mock with jest.mock or __mocks__
  • elevation (Android) and shadow* (iOS) are separate systems — both needed for cross-platform shadow
  • SafeAreaProvider must wrap the entire app at root level — not per-screen