Skip to main content
Flexbox Layout

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

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',
  },
});
###Why StyleSheet.create()?

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

// ❌ 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:
// 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
}

Flexbox Cheat Sheet

┌──────────────────────────────────────────────────────────────────┐
│                      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

// 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)

<View style={{ position: 'relative' }}>
  <Text style={{ top: 10, left: 20 }}>
    Offset from normal position
  </Text>
</View>

Absolute Positioning

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

// 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

<View style={{ width: 100, height: 100 }} />

Percentage Dimensions

<View style={{ width: '50%', height: '100%' }} />

Dimensions API

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

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)

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

const styles = StyleSheet.create({
  card: {
    // iOS shadow properties
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.25,
    shadowRadius: 3.84,
  },
});

Android Elevation

const styles = StyleSheet.create({
  card: {
    // Android elevation
    elevation: 5,
  },
});

Cross-Platform Shadow

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

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

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 wins

Conditional Styles

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

// 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

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

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

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

<View style={{ opacity: 0.5 }}>
  <Text>Semi-transparent</Text>
</View>

Hands-On: Responsive Login Screen

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