Styling in React Native
React Native uses JavaScript objects for styling, not CSS. Styles are created using
StyleSheet.create() for performance optimization. The layout system is
based on Flexbox, with some differences from web.
StyleSheet Basics
import { View, Text, StyleSheet } from 'react-native';
function StyledComponent() {
return (
<View style={styles.container}>
<Text style={styles.title}>Hello World</Text>
{/* Combining styles */}
<Text style={[styles.text, styles.bold]}>Bold text</Text>
{/* Conditional styles */}
<Text style={[styles.text, isActive && styles.active]}>
Conditional
</Text>
{/* Inline styles (avoid for performance) */}
<Text style={{ color: 'red', fontSize: 16 }}>
Inline style
</Text>
</View>
);
}
// Always use StyleSheet.create for performance
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#fff',
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#333',
marginBottom: 16,
},
text: {
fontSize: 16,
color: '#666',
},
bold: {
fontWeight: 'bold',
},
active: {
color: '#007AFF',
},
});
🔑 Key Differences from CSS
- • Properties are camelCase (fontSize, not font-size)
- • Values are numbers (no 'px' needed) or strings
- • No inheritance (except Text within Text)
- • No cascade - styles don't flow to children
- • Flexbox defaults to column direction
Flexbox Layout
import { View, Text, StyleSheet } from 'react-native';
// Flexbox in React Native (defaults to column)
function FlexboxExample() {
return (
<View style={styles.container}>
{/* Row layout */}
<View style={styles.row}>
<View style={styles.box1} />
<View style={styles.box2} />
<View style={styles.box3} />
</View>
{/* Flex grow */}
<View style={styles.row}>
<View style={[styles.box, { flex: 1 }]} />
<View style={[styles.box, { flex: 2 }]} />
<View style={[styles.box, { flex: 1 }]} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
row: {
flexDirection: 'row', // 'row' | 'column' | 'row-reverse' | 'column-reverse'
justifyContent: 'space-between', // main axis
alignItems: 'center', // cross axis
marginBottom: 20,
},
box: {
height: 60,
backgroundColor: '#3498db',
},
box1: { width: 60, height: 60, backgroundColor: '#e74c3c' },
box2: { width: 60, height: 60, backgroundColor: '#2ecc71' },
box3: { width: 60, height: 60, backgroundColor: '#9b59b6' },
});
Common Flexbox Patterns
const layoutPatterns = StyleSheet.create({
// Center content
centered: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
// Header with left and right items
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 16,
height: 56,
},
// Card with image and content
card: {
flexDirection: 'row',
padding: 16,
backgroundColor: '#fff',
borderRadius: 8,
},
cardImage: {
width: 80,
height: 80,
borderRadius: 8,
},
cardContent: {
flex: 1,
marginLeft: 16,
},
// Footer fixed at bottom
footerContainer: {
flex: 1,
justifyContent: 'space-between',
},
footer: {
padding: 16,
backgroundColor: '#f0f0f0',
},
// Grid layout (2 columns)
grid: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
},
gridItem: {
width: '48%', // ~half minus gap
marginBottom: 16,
},
});
Responsive Design
import { View, Text, StyleSheet, Dimensions, useWindowDimensions } from 'react-native';
// Get screen dimensions
const { width, height } = Dimensions.get('window');
// Hook for dynamic dimensions (handles rotation)
function ResponsiveComponent() {
const { width, height } = useWindowDimensions();
const isLandscape = width > height;
const isTablet = width >= 768;
return (
<View style={[
styles.container,
isLandscape && styles.landscapeContainer,
isTablet && styles.tabletContainer,
]}>
<View style={[
styles.card,
{ width: isTablet ? '45%' : '100%' }
]}>
<Text>Responsive Card</Text>
</View>
</View>
);
}
// Percentage-based widths
const styles = StyleSheet.create({
container: {
flex: 1,
padding: width * 0.05, // 5% of screen width
},
landscapeContainer: {
flexDirection: 'row',
},
tabletContainer: {
paddingHorizontal: 40,
},
card: {
padding: 16,
backgroundColor: '#fff',
borderRadius: 8,
},
});
// Create responsive values helper
const scale = (size) => (width / 375) * size; // Base on iPhone 8
const moderateScale = (size, factor = 0.5) =>
size + (scale(size) - size) * factor;
Platform-Specific Styles
import { Platform, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
...Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
// Platform-specific values
text: {
fontFamily: Platform.OS === 'ios' ? 'Helvetica' : 'Roboto',
fontSize: 16,
paddingTop: Platform.OS === 'ios' ? 20 : 0,
},
// Status bar padding
safeTop: {
paddingTop: Platform.OS === 'ios' ? 44 : 24,
},
});
// Or use Platform.select for values
const fontFamily = Platform.select({
ios: 'San Francisco',
android: 'Roboto',
default: 'System',
});
Safe Area Handling
import { SafeAreaView, StyleSheet } from 'react-native';
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
// Using SafeAreaView (basic)
function BasicSafeArea() {
return (
<SafeAreaView style={styles.container}>
<Text>Content inside safe area</Text>
</SafeAreaView>
);
}
// Using react-native-safe-area-context (recommended)
function App() {
return (
<SafeAreaProvider>
<MyScreen />
</SafeAreaProvider>
);
}
function MyScreen() {
const insets = useSafeAreaInsets();
return (
<View style={[styles.container, {
paddingTop: insets.top,
paddingBottom: insets.bottom,
paddingLeft: insets.left,
paddingRight: insets.right,
}]}>
<Text>Content respects notch and home indicator</Text>
</View>
);
}
// For bottom tabs or fixed footers
function FixedFooter() {
const insets = useSafeAreaInsets();
return (
<View style={[styles.footer, { paddingBottom: insets.bottom }]}>
<Button title="Submit" />
</View>
);
}
Common Style Properties
const commonStyles = StyleSheet.create({
// Box model
box: {
width: 100, // number or percentage string
height: 100,
minWidth: 50,
maxWidth: 200,
padding: 16, // all sides
paddingHorizontal: 16, // left and right
paddingVertical: 8, // top and bottom
margin: 8,
marginTop: 16,
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
borderTopLeftRadius: 16,
},
// Text styles
text: {
fontSize: 16,
fontWeight: 'bold', // 'normal', 'bold', '100'-'900'
fontStyle: 'italic',
color: '#333',
textAlign: 'center', // 'left', 'right', 'center', 'justify'
textTransform: 'uppercase',
letterSpacing: 1,
lineHeight: 24,
textDecorationLine: 'underline',
},
// Positioning
positioned: {
position: 'absolute', // 'relative' (default) or 'absolute'
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: 10,
},
// Effects
effects: {
opacity: 0.8,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
overflow: 'hidden', // 'visible', 'hidden', 'scroll'
},
});
Styled Components Pattern
// Create reusable styled components
// Typography components
const Typography = {
H1: ({ children, style }) => (
<Text style={[typography.h1, style]}>{children}</Text>
),
H2: ({ children, style }) => (
<Text style={[typography.h2, style]}>{children}</Text>
),
Body: ({ children, style }) => (
<Text style={[typography.body, style]}>{children}</Text>
),
};
const typography = StyleSheet.create({
h1: { fontSize: 32, fontWeight: 'bold', color: '#111' },
h2: { fontSize: 24, fontWeight: '600', color: '#333' },
body: { fontSize: 16, lineHeight: 24, color: '#666' },
});
// Container components
const Container = ({ children, style }) => (
<View style={[containers.default, style]}>{children}</View>
);
const Card = ({ children, style }) => (
<View style={[containers.card, style]}>{children}</View>
);
const containers = StyleSheet.create({
default: { flex: 1, padding: 16 },
card: {
backgroundColor: '#fff',
borderRadius: 12,
padding: 16,
...Platform.select({
ios: { shadowColor: '#000', shadowOpacity: 0.1, shadowRadius: 10 },
android: { elevation: 3 },
}),
},
});
// Usage
<Container>
<Typography.H1>Welcome</Typography.H1>
<Card>
<Typography.Body>Card content here</Typography.Body>
</Card>
</Container>