Styling and Nativewind
Styling approaches in React Native including StyleSheet, NativeWind, and responsive design patterns
You are an expert in styling for building cross-platform mobile apps with React Native. ## Key Points - Use `StyleSheet.create` for static styles — it validates at creation time and enables potential optimizations. - Prefer `gap` over manual margins for spacing between flex children (supported in React Native 0.71+). - Use NativeWind for rapid prototyping and design-system consistency; use raw StyleSheet when you need fine-grained control or dynamic computed styles. - Keep a centralized theme file with design tokens for colors, spacing, and typography. - Use `useWindowDimensions` (not `Dimensions.get`) as it updates on rotation and window resizing. - Apply `flex: 1` to scrollable containers so they fill available space correctly. - **Percentage widths behave differently**: Percentage-based sizing requires the parent to have a defined dimension. Use `flex` for proportional layouts instead. - **Missing `overflow: "hidden"` for border radius**: On Android, `borderRadius` on a parent does not clip children by default. Add `overflow: "hidden"`. - **Shadow differences across platforms**: iOS uses `shadow*` properties; Android uses `elevation`. Always define both or use a cross-platform shadow library. - **Text styling inheritance does not work**: Unlike CSS, text styles do not inherit from parent Views. Every `<Text>` component must have its own style. - **NativeWind arbitrary values**: Use bracket notation for one-off values: `className="w-[327px] mt-[14px]"`. But prefer design tokens when possible. ## Quick Example ```bash npx expo install nativewind tailwindcss react-native-reanimated react-native-safe-area-context ``` ```css /* global.css */ @tailwind base; @tailwind components; @tailwind utilities; ```
skilldb get react-native-skills/Styling and NativewindFull skill: 335 linesStyling and NativeWind — React Native
You are an expert in styling for building cross-platform mobile apps with React Native.
Core Philosophy
React Native styling looks like CSS but behaves differently in important ways. The layout engine is Yoga (a Flexbox implementation), which defaults to flexDirection: "column" (unlike CSS's row default). Text styles do not inherit from parent Views. Percentage-based sizing requires explicit parent dimensions. These differences trip up developers coming from web CSS, and learning them early prevents hours of debugging layout issues that "should work" based on web experience.
NativeWind bridges the gap between Tailwind's utility-first approach and React Native's StyleSheet system. It compiles Tailwind classes into optimized StyleSheet objects at build time, meaning there is no runtime performance penalty compared to hand-written StyleSheet.create. The benefit is development speed: className="bg-white rounded-2xl p-4 shadow-sm" communicates intent instantly, while the equivalent StyleSheet object requires reading five separate property declarations. For teams already familiar with Tailwind from web development, NativeWind eliminates the context switch.
Design tokens (a centralized theme with named colors, spacing values, typography scales, and border radii) are essential for maintaining visual consistency across a React Native app. When every component hardcodes its own shade of gray or padding value, the app accumulates visual inconsistencies that are impossible to fix without touching every file. A single theme.ts file that exports named constants means changing the brand color or spacing scale is a one-line change that propagates everywhere.
Anti-Patterns
-
Using percentage widths without explicit parent dimensions: In React Native, percentage-based sizing requires the parent to have a defined dimension in that axis. A
width: "50%"inside a parent with no explicit width produces undefined behavior. Useflexfor proportional layouts, which works reliably with Yoga's layout algorithm. -
Forgetting overflow hidden for borderRadius on Android: On iOS,
borderRadiusclips children automatically. On Android, it does not -- children render outside the rounded corners, producing visually broken cards and buttons. Always addoverflow: "hidden"when usingborderRadiuson containers with children. -
Defining platform-specific shadows only for one platform: iOS uses
shadowColor,shadowOffset,shadowOpacity, andshadowRadius. Android useselevation. Defining only one set of properties means shadows appear on one platform and are invisible on the other. Always define both, or use NativeWind's cross-platformshadow-*classes. -
Relying on text style inheritance from parent Views: Unlike web CSS where a
coloron adivis inherited by all descendant text, React NativeTextcomponents do not inherit styles from parentViewcomponents. EachTextmust have its own explicit style, or you must nestTextinsideTextfor inheritance to work. -
Using Dimensions.get instead of useWindowDimensions:
Dimensions.get("window")returns a static value that does not update when the device rotates or the window resizes (on iPads and foldables).useWindowDimensions()is a hook that re-renders the component with updated dimensions, making responsive layouts work correctly across orientation changes.
Overview
React Native uses a JavaScript-based styling system inspired by CSS but implemented through Yoga (a Flexbox layout engine). Styles are defined as JavaScript objects via StyleSheet.create. NativeWind brings Tailwind CSS utility classes to React Native, compiling them to optimized StyleSheet objects at build time. This skill covers the built-in StyleSheet API, NativeWind v4, and responsive design strategies.
Core Concepts
StyleSheet Basics
import { StyleSheet, View, Text } from "react-native";
function Card({ title, children }: { title: string; children: React.ReactNode }) {
return (
<View style={styles.card}>
<Text style={styles.title}>{title}</Text>
{children}
</View>
);
}
const styles = StyleSheet.create({
card: {
backgroundColor: "#ffffff",
borderRadius: 12,
padding: 16,
marginVertical: 8,
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3, // Android shadow
},
title: {
fontSize: 18,
fontWeight: "600",
color: "#1a1a2e",
marginBottom: 8,
},
});
Flexbox Layout (Yoga)
React Native defaults to flexDirection: "column" (unlike CSS which defaults to row).
const styles = StyleSheet.create({
row: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
},
centered: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
spacer: {
flex: 1, // Takes remaining space
},
fixed: {
width: 100,
height: 50,
},
});
NativeWind v4 Setup
npx expo install nativewind tailwindcss react-native-reanimated react-native-safe-area-context
// tailwind.config.js
module.exports = {
content: ["./app/**/*.{js,tsx}", "./components/**/*.{js,tsx}"],
presets: [require("nativewind/preset")],
theme: {
extend: {
colors: {
brand: {
50: "#eef2ff",
500: "#6366f1",
600: "#4f46e5",
900: "#312e81",
},
},
},
},
};
/* global.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
// app/_layout.tsx
import "../global.css";
export default function RootLayout() {
return <Stack />;
}
Implementation Patterns
NativeWind Component Styling
import { View, Text, Pressable } from "react-native";
function ProfileCard({ name, role }: { name: string; role: string }) {
return (
<View className="bg-white rounded-2xl p-4 mx-4 my-2 shadow-sm">
<View className="flex-row items-center gap-3">
<View className="w-12 h-12 rounded-full bg-brand-500 items-center justify-center">
<Text className="text-white text-lg font-bold">
{name.charAt(0)}
</Text>
</View>
<View className="flex-1">
<Text className="text-lg font-semibold text-gray-900">{name}</Text>
<Text className="text-sm text-gray-500">{role}</Text>
</View>
</View>
</View>
);
}
Dark Mode with NativeWind
import { useColorScheme } from "nativewind";
function ThemedScreen() {
const { colorScheme, toggleColorScheme } = useColorScheme();
return (
<View className="flex-1 bg-white dark:bg-gray-900 p-4">
<Text className="text-gray-900 dark:text-gray-100 text-xl font-bold">
Current theme: {colorScheme}
</Text>
<Pressable
onPress={toggleColorScheme}
className="mt-4 bg-brand-500 dark:bg-brand-600 rounded-lg py-3 px-6"
>
<Text className="text-white text-center font-semibold">
Toggle Theme
</Text>
</Pressable>
</View>
);
}
Platform-Specific Styling
import { Platform, StyleSheet } from "react-native";
const styles = StyleSheet.create({
container: {
...Platform.select({
ios: {
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.15,
shadowRadius: 6,
},
android: {
elevation: 4,
},
}),
},
});
// With NativeWind — use platform prefixes
function PlatformCard() {
return (
<View className="ios:shadow-md android:elevation-4 bg-white rounded-xl p-4">
<Text>Platform-aware card</Text>
</View>
);
}
Responsive Design
import { useWindowDimensions } from "react-native";
function ResponsiveGrid({ items }: { items: Item[] }) {
const { width } = useWindowDimensions();
const numColumns = width > 768 ? 3 : width > 480 ? 2 : 1;
return (
<FlatList
data={items}
numColumns={numColumns}
key={numColumns} // Force re-render when columns change
renderItem={({ item }) => (
<View style={{ flex: 1 / numColumns, padding: 8 }}>
<ItemCard item={item} />
</View>
)}
/>
);
}
Design Tokens and Theme Objects
// theme.ts
export const theme = {
colors: {
primary: "#6366f1",
primaryDark: "#4f46e5",
background: "#f8fafc",
surface: "#ffffff",
text: "#0f172a",
textSecondary: "#64748b",
border: "#e2e8f0",
error: "#ef4444",
success: "#22c55e",
},
spacing: {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
},
radii: {
sm: 6,
md: 12,
lg: 16,
full: 9999,
},
typography: {
h1: { fontSize: 28, fontWeight: "700" as const, lineHeight: 34 },
h2: { fontSize: 22, fontWeight: "600" as const, lineHeight: 28 },
body: { fontSize: 16, fontWeight: "400" as const, lineHeight: 24 },
caption: { fontSize: 12, fontWeight: "400" as const, lineHeight: 16 },
},
} as const;
Combining Styles
// Style composition — array syntax
<View style={[styles.base, isActive && styles.active, style]}>
// NativeWind — template literals
<View className={`p-4 rounded-lg ${isActive ? "bg-brand-500" : "bg-gray-200"}`}>
// Using clsx with NativeWind
import { clsx } from "clsx";
function Button({ variant, children }: ButtonProps) {
return (
<Pressable
className={clsx(
"rounded-lg py-3 px-6 items-center",
variant === "primary" && "bg-brand-500",
variant === "secondary" && "bg-gray-200",
variant === "ghost" && "bg-transparent"
)}
>
<Text
className={clsx(
"font-semibold",
variant === "primary" ? "text-white" : "text-gray-900"
)}
>
{children}
</Text>
</Pressable>
);
}
Best Practices
- Use
StyleSheet.createfor static styles — it validates at creation time and enables potential optimizations. - Prefer
gapover manual margins for spacing between flex children (supported in React Native 0.71+). - Use NativeWind for rapid prototyping and design-system consistency; use raw StyleSheet when you need fine-grained control or dynamic computed styles.
- Keep a centralized theme file with design tokens for colors, spacing, and typography.
- Use
useWindowDimensions(notDimensions.get) as it updates on rotation and window resizing. - Apply
flex: 1to scrollable containers so they fill available space correctly.
Common Pitfalls
- Percentage widths behave differently: Percentage-based sizing requires the parent to have a defined dimension. Use
flexfor proportional layouts instead. - Missing
overflow: "hidden"for border radius: On Android,borderRadiuson a parent does not clip children by default. Addoverflow: "hidden". - Shadow differences across platforms: iOS uses
shadow*properties; Android useselevation. Always define both or use a cross-platform shadow library. - Text styling inheritance does not work: Unlike CSS, text styles do not inherit from parent Views. Every
<Text>component must have its own style. - NativeWind arbitrary values: Use bracket notation for one-off values:
className="w-[327px] mt-[14px]". But prefer design tokens when possible.
Install this skill directly: skilldb add react-native-skills
Related Skills
Reanimated Animations
High-performance animations in React Native using Reanimated and Gesture Handler
Eas Build Ota Updates
Deploying React Native apps with EAS Build, app store submission, and OTA updates via EAS Update
Expo Managed Workflow
Expo managed workflow for rapid React Native development with minimal native configuration
Native Modules Turbo Modules
Creating native modules and Turbo Modules to bridge platform-specific functionality into React Native
React Navigation Patterns
React Navigation patterns for stack, tab, drawer, and nested navigators in React Native
Offline Storage
Offline storage strategies in React Native using AsyncStorage, MMKV, and WatermelonDB