Styling & Layout in React Native
React Native uses a subset of CSS implemented in JavaScript. Styling is done using the StyleSheet API, and layout is powered by Flexbox (with some differences from web CSS).StyleSheet API
Creating Styles
Copy
import { StyleSheet, View, Text } from 'react-native';
const App = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>Hello, World!</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
padding: 20,
},
text: {
fontSize: 18,
color: '#333',
fontWeight: 'bold',
},
});
Performance
Styles are converted to plain objects and sent to native side only once
Validation
Catches typos and invalid values at runtime
Type Safety
Better TypeScript autocomplete and type checking
Optimization
Styles can be optimized and reused
Inline Styles
Copy
// ❌ Don't do this (creates new object every render)
<View style={{ flex: 1, padding: 20 }} />
// ✅ Do this (style object is reused)
const styles = StyleSheet.create({
container: { flex: 1, padding: 20 },
});
<View style={styles.container} />
// ⚠️ Only use inline for dynamic values
<View style={{ marginTop: dynamicValue }} />
Flexbox Layout
React Native uses Flexbox for layout, with some key differences from CSS:- React Native
- Web CSS
Copy
// Default values differ from web!
{
flexDirection: 'column', // Web default: 'row'
alignItems: 'stretch', // Same as web
alignContent: 'flex-start', // Same as web
flexShrink: 1, // Web default: 0
}
Copy
/* Web defaults */
{
flex-direction: row;
align-items: stretch;
align-content: flex-start;
flex-shrink: 0;
}
Flexbox Cheat Sheet
Copy
┌──────────────────────────────────────────────────────────────────┐
│ Flexbox Properties │
├──────────────────────────────────────────────────────────────────┤
│ │
│ flex-direction: 'column' | 'row' | 'column-reverse' | 'row-reverse'
│ ────────────── │
│ column: row: │
│ ┌───┐ ┌───┬───┬───┐ │
│ │ 1 │ │ 1 │ 2 │ 3 │ │
│ ├───┤ └───┴───┴───┘ │
│ │ 2 │ │
│ ├───┤ │
│ │ 3 │ │
│ └───┘ │
│ │
│ justify-content: Aligns items along main axis │
│ ───────────────── │
│ 'flex-start' | 'center' | 'flex-end' | 'space-between' | │
│ 'space-around' | 'space-evenly' │
│ │
│ flex-start: center: flex-end: │
│ ┌─┬─┬─┐ ┌─┬─┬─┐ ┌─┬─┬─┐ │
│ │1│2│3│ │1│2│3│ │1│2│3│ │
│ └─┴─┴─┘ └─┴─┴─┘ └─┴─┴─┘ │
│ │
│ space-between: space-around: space-evenly: │
│ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ │
│ │1│ │2│ │3│ │1│ │2│ │3│ │1│ │2│ │3│ │
│ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ │
│ │
│ align-items: Aligns items along cross axis │
│ ──────────── │
│ 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline' │
│ │
│ align-self: Override align-items for single item │
│ ──────────── │
│ Same values as align-items │
│ │
│ flex: Defines how item grows/shrinks │
│ ──── │
│ flex: 1 → Takes all available space │
│ flex: 2 → Takes 2x more space than flex: 1 │
│ │
└──────────────────────────────────────────────────────────────────┘
Flexbox Examples
Copy
// Center content
const CenteredView = () => (
<View style={styles.centered}>
<Text>Perfectly centered!</Text>
</View>
);
const styles = StyleSheet.create({
centered: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
// Horizontal layout
const HorizontalBoxes = () => (
<View style={styles.container}>
<View style={[styles.box, { backgroundColor: 'red' }]} />
<View style={[styles.box, { backgroundColor: 'green' }]} />
<View style={[styles.box, { backgroundColor: 'blue' }]} />
</View>
);
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
justifyContent: 'space-between',
padding: 20,
},
box: {
width: 100,
height: 100,
},
});
// Flex grow
const FlexGrow = () => (
<View style={{ flex: 1, flexDirection: 'column' }}>
<View style={{ flex: 1, backgroundColor: 'red' }} />
<View style={{ flex: 2, backgroundColor: 'green' }} />
<View style={{ flex: 1, backgroundColor: 'blue' }} />
</View>
);
Positioning
React Native supports both relative and absolute positioning.Relative Positioning (Default)
Copy
<View style={{ position: 'relative' }}>
<Text style={{ top: 10, left: 20 }}>
Offset from normal position
</Text>
</View>
Absolute Positioning
Copy
const Overlay = () => (
<View style={{ flex: 1 }}>
<Text>Background content</Text>
<View style={styles.overlay}>
<Text style={styles.overlayText}>Overlay</Text>
</View>
</View>
);
const styles = StyleSheet.create({
overlay: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
},
overlayText: {
color: '#fff',
fontSize: 24,
},
});
Position Shortcuts
Copy
// These two are equivalent:
<View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }} />
<View style={{ ...StyleSheet.absoluteFillObject }} />
Dimensions & Responsive Design
Fixed Dimensions
Copy
<View style={{ width: 100, height: 100 }} />
Percentage Dimensions
Copy
<View style={{ width: '50%', height: '100%' }} />
Dimensions API
Copy
import { Dimensions } from 'react-native';
const { width, height } = Dimensions.get('window');
// or Dimensions.get('screen')
<View style={{ width: width * 0.9, height: height * 0.5 }} />
Limitation:
Dimensions.get() only returns initial dimensions. For responsive updates, use useWindowDimensions hook or listen to dimension change events.useWindowDimensions Hook
Copy
import { useWindowDimensions, View, Text } from 'react-native';
const ResponsiveComponent = () => {
const { width, height, scale, fontScale } = useWindowDimensions();
return (
<View style={{ width: width * 0.9 }}>
<Text>Window width: {width}</Text>
<Text>Window height: {height}</Text>
<Text>Scale: {scale}</Text>
</View>
);
};
Media Queries (Custom Hook)
Copy
import { useWindowDimensions } from 'react-native';
const useResponsive = () => {
const { width } = useWindowDimensions();
return {
isSmall: width < 375,
isMedium: width >= 375 && width < 768,
isLarge: width >= 768,
};
};
// Usage
const App = () => {
const { isSmall, isMedium, isLarge } = useResponsive();
return (
<View style={{ padding: isSmall ? 10 : isLarge ? 30 : 20 }}>
<Text style={{ fontSize: isSmall ? 14 : isLarge ? 20 : 16 }}>
Responsive text
</Text>
</View>
);
};
Shadows & Elevation
iOS Shadow
Copy
const styles = StyleSheet.create({
card: {
// iOS shadow properties
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
},
});
Android Elevation
Copy
const styles = StyleSheet.create({
card: {
// Android elevation
elevation: 5,
},
});
Cross-Platform Shadow
Copy
import { Platform, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
card: {
backgroundColor: '#fff',
borderRadius: 8,
padding: 16,
// Platform-specific shadow
...Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 3.84,
},
android: {
elevation: 5,
},
}),
},
});
Text Styling
Copy
const styles = StyleSheet.create({
text: {
// Font
fontFamily: 'System',
fontSize: 16,
fontWeight: 'bold', // '100' to '900', 'normal', 'bold'
fontStyle: 'italic', // 'normal' | 'italic'
// Color
color: '#333',
// Alignment
textAlign: 'center', // 'left' | 'right' | 'center' | 'justify'
textAlignVertical: 'center', // Android only
// Decoration
textDecorationLine: 'underline', // 'none' | 'underline' | 'line-through' | 'underline line-through'
textDecorationStyle: 'solid', // 'solid' | 'double' | 'dotted' | 'dashed'
textDecorationColor: '#000',
// Transform
textTransform: 'uppercase', // 'none' | 'uppercase' | 'lowercase' | 'capitalize'
// Spacing
letterSpacing: 1,
lineHeight: 24,
// Shadow (iOS)
textShadowColor: 'rgba(0, 0, 0, 0.75)',
textShadowOffset: { width: -1, height: 1 },
textShadowRadius: 10,
},
});
Style Composition
Combining Styles
Copy
const App = () => {
return (
<View style={[styles.box, styles.rounded, { marginTop: 20 }]}>
<Text>Multiple styles</Text>
</View>
);
};
const styles = StyleSheet.create({
box: {
width: 100,
height: 100,
backgroundColor: 'blue',
},
rounded: {
borderRadius: 10,
},
});
Style Priority: Later styles override earlier ones.
[styles.base, styles.override] → override winsConditional Styles
Copy
const Button = ({ primary, disabled }) => {
return (
<View
style={[
styles.button,
primary && styles.primaryButton,
disabled && styles.disabledButton,
]}
>
<Text style={styles.buttonText}>Press me</Text>
</View>
);
};
const styles = StyleSheet.create({
button: {
padding: 15,
borderRadius: 8,
backgroundColor: '#ddd',
},
primaryButton: {
backgroundColor: '#007AFF',
},
disabledButton: {
opacity: 0.5,
},
buttonText: {
color: '#fff',
textAlign: 'center',
},
});
Theming
Simple Theme
Copy
// theme.ts
export const colors = {
primary: '#007AFF',
secondary: '#5856D6',
background: '#fff',
text: '#000',
textSecondary: '#666',
border: '#ddd',
error: '#FF3B30',
success: '#34C759',
};
export const spacing = {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
};
export const fontSize = {
small: 12,
regular: 16,
large: 20,
xlarge: 24,
};
// Usage
import { colors, spacing, fontSize } from './theme';
const styles = StyleSheet.create({
container: {
backgroundColor: colors.background,
padding: spacing.md,
},
title: {
fontSize: fontSize.large,
color: colors.text,
},
});
Dark Mode Support
Copy
import { useColorScheme } from 'react-native';
const App = () => {
const colorScheme = useColorScheme();
const isDark = colorScheme === 'dark';
return (
<View style={[styles.container, isDark && styles.darkContainer]}>
<Text style={[styles.text, isDark && styles.darkText]}>
Hello, {isDark ? 'Dark' : 'Light'} Mode!
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
darkContainer: {
backgroundColor: '#000',
},
text: {
color: '#000',
},
darkText: {
color: '#fff',
},
});
Border Styling
Copy
const styles = StyleSheet.create({
box: {
// All borders
borderWidth: 1,
borderColor: '#ddd',
borderStyle: 'solid', // 'solid' | 'dotted' | 'dashed'
// Individual borders
borderTopWidth: 1,
borderRightWidth: 1,
borderBottomWidth: 1,
borderLeftWidth: 1,
borderTopColor: 'red',
borderRightColor: 'green',
borderBottomColor: 'blue',
borderLeftColor: 'yellow',
// Border radius
borderRadius: 10,
// Individual corners
borderTopLeftRadius: 10,
borderTopRightRadius: 10,
borderBottomRightRadius: 10,
borderBottomLeftRadius: 10,
},
});
Transform
Copy
const styles = StyleSheet.create({
transformed: {
transform: [
{ translateX: 50 },
{ translateY: 100 },
{ rotate: '45deg' },
{ rotateX: '45deg' },
{ rotateY: '45deg' },
{ rotateZ: '45deg' },
{ scale: 1.5 },
{ scaleX: 1.5 },
{ scaleY: 1.5 },
{ skewX: '45deg' },
{ skewY: '45deg' },
],
},
});
Opacity
Copy
<View style={{ opacity: 0.5 }}>
<Text>Semi-transparent</Text>
</View>
Hands-On: Responsive Login Screen
Copy
import React, { useState } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
useWindowDimensions,
KeyboardAvoidingView,
Platform,
} from 'react-native';
const LoginScreen = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const { width } = useWindowDimensions();
const isSmallScreen = width < 375;
const isMediumScreen = width >= 375 && width < 768;
const containerPadding = isSmallScreen ? 20 : isMediumScreen ? 30 : 40;
const titleSize = isSmallScreen ? 24 : isMediumScreen ? 28 : 32;
return (
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={styles.container}
>
<View style={[styles.formContainer, { padding: containerPadding }]}>
<Text style={[styles.title, { fontSize: titleSize }]}>
Welcome Back
</Text>
<Text style={styles.subtitle}>Sign in to continue</Text>
<TextInput
style={styles.input}
placeholder="Email"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
/>
<TextInput
style={styles.input}
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<TouchableOpacity style={styles.button} activeOpacity={0.8}>
<Text style={styles.buttonText}>Sign In</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.forgotPassword}>
<Text style={styles.forgotPasswordText}>Forgot Password?</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
formContainer: {
flex: 1,
justifyContent: 'center',
},
title: {
fontWeight: 'bold',
color: '#333',
marginBottom: 8,
},
subtitle: {
fontSize: 16,
color: '#666',
marginBottom: 30,
},
input: {
height: 50,
backgroundColor: '#fff',
borderRadius: 10,
paddingHorizontal: 15,
fontSize: 16,
marginBottom: 15,
borderWidth: 1,
borderColor: '#ddd',
},
button: {
height: 50,
backgroundColor: '#007AFF',
borderRadius: 10,
justifyContent: 'center',
alignItems: 'center',
marginTop: 10,
...Platform.select({
ios: {
shadowColor: '#007AFF',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 5,
},
android: {
elevation: 4,
},
}),
},
buttonText: {
color: '#fff',
fontSize: 18,
fontWeight: '600',
},
forgotPassword: {
marginTop: 20,
alignSelf: 'center',
},
forgotPasswordText: {
color: '#007AFF',
fontSize: 14,
},
});
export default LoginScreen;
Summary
In this module, you learned: ✅ StyleSheet API and style creation ✅ Flexbox layout with React Native differences ✅ Absolute and relative positioning ✅ Responsive design with Dimensions and useWindowDimensions ✅ Platform-specific styling (shadows, elevation) ✅ Text styling, borders, and transforms ✅ Style composition and theming ✅ Building a responsive login screen Next up: Navigation between screens!Next: Navigation
Learn React Navigation and build multi-screen apps