Styling Best Practices
A condensed summary of the 25 most important best practices drawn from every page in this section.
Search across all documentation pages
A condensed summary of the 25 most important best practices drawn from every page in this section.
Cache static styles with StyleSheet.create: Move layout, spacing, and color rules that do not change per render into a stylesheet - inline object literals allocate on every render and defeat native caching.
Compose with style arrays: Layer base, state, and overrides as [styles.card, pressed && styles.cardPressed, props.style] - order matters; document whether caller or component wins.
Remember the column default: React Native flex defaults to column - set flexDirection: "row" explicitly whenever you expect horizontal layout.
Fill screens with flex: 1, not height: "100%": Percentage height needs a parent with defined height; flex: 1 on the root view (inside safe area) is the reliable viewport fill pattern.
Use gap for sibling spacing: Prefer gap on flex containers over margin hacks on first/last child - fewer edge-case bugs when items mount and unmount.
Wrap the app in SafeAreaProvider once: Read insets via useSafeAreaInsets or SafeAreaView at the screen root - do not nest safe-area padding on every child.
Pick safe-area edges deliberately: Full-bleed hero images need edges={['bottom']} or manual bottom inset only - applying all edges on nested wrappers doubles padding.
Plan for edge-to-edge Android (RN 0.86): Status bar and navigation bar insets change on modern Android - test on API 35+ devices, not only classic inset models.
Theme with semantic tokens: Name colors surface, textPrimary, borderSubtle - map tokens to light/dark palettes instead of sprinkling #111 and #fff through feature code.
Sync chrome with useColorScheme: Drive StatusBar, tab bars, and navigation headers from the active scheme - hardcoded light status bar icons vanish on dark backgrounds.
Breakpoint on window width, not device class: useWindowDimensions().width >= 768 works for Android tablets and foldables - Platform.isPad alone is insufficient.
Avoid web-only layout assumptions: No media queries or vh - use hooks, percentage widths, and flexWrap for responsive phone/tablet layouts.
Stabilize list wrapper styles: contentContainerStyle={{ padding: 16 }} on FlatList recreates every parent render - hoist to StyleSheet.create or useMemo.
Memoize row components, not just styles: memo(Row) fails when parents pass fresh inline style objects - pass styles.row references from the stylesheet.
Animate transform and opacity, not layout: Driving height from setState every frame triggers Yoga layout each tick - use Reanimated for motion; reserve LayoutAnimation for discrete toggles.
Shadow on a wrapper, clip on the inner view: overflow: "hidden" on the same view that carries iOS shadow* props clips the shadow - split outer shadow wrapper and inner content.
Always set backgroundColor on elevated surfaces: iOS shadows and Android elevation need an opaque surface color - transparent cards produce invisible depth.
Pair iOS shadow with Android elevation: shadowColor is ignored on Android - use Platform.select in StyleSheet.create for cross-platform cards.
Use StyleSheet.hairlineWidth for dividers: borderWidth: 1 looks heavy on retina screens - hairline width tracks pixel density.
Load fonts before first meaningful paint: useFonts + SplashScreen.preventAutoHideAsync - return null at the root until fonts load or error; never flash system font on brand screens.
Register each font weight explicitly: fontWeight: "700" with a single custom family often fails - load Inter-Bold.ttf and set fontFamily: "Inter-Bold" directly.
Cap scaling on dense UI: Apply maxFontSizeMultiplier on toolbars and chips; allow full dynamic type on body copy - accessibility without breaking layout.
Define a typography scale in tokens: Map display, title, body, and label to fontSize + lineHeight pairs - screens reference roles, not raw numbers.
Profile before adopting a styling library: NativeWind, Tamagui, and Unistyles solve scale problems - a handful of StyleSheet screens do not need the bundle and config cost.
Audit public styles in code review: New inline styles in list hot paths and one-off hex colors are regressions - enforce tokens and stylesheet usage in CI lint rules where possible.
StyleSheet.create or a token module.StyleSheet + tokens stay readable.shadow* props - add elevation and a solid backgroundColor.useWindowDimensions() - it re-renders on rotation and split-screen, unlike a one-shot Dimensions.get().light, dark) and select at runtime - or use a small useTheme() hook that returns the active palette.StyleSheet.create blocks per scheme unless profiling proves it necessary.Stack versions: This page was written for React 19.2.3, React Native 0.86.0, and Expo SDK 57 (
expo~57.0.4).
Reviewed by Chris St. John·Last updated Jul 16, 2026