DailyDevDiet

logo - dailydevdiet

Learn. Build. Innovate. Elevate your coding skills with dailydevdiet!

Chapter 8: Styling in React Native

Styling in React Native

Styling in React Native is a crucial aspect of React Native development that determines how your app looks and feels. Unlike web development where you use CSS, React Native uses a JavaScript-based styling system that provides a subset of CSS properties optimized for mobile development.

Introduction to Styling in React Native

React Native styling is based on a subset of CSS, but with important differences:

  • Styles are written in JavaScript objects
  • CSS properties use camelCase instead of kebab-case
  • No cascade (styles don’t inherit like CSS)
  • Default layout system is Flexbox
  • Dimensions are unitless (density-independent pixels)

Basic Styling Example

import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

function MyComponent() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Hello, React Native!</Text>
      <Text style={styles.subtitle}>Styling is easy</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f0f0f0',
    padding: 20,
    justifyContent: 'center',
    alignItems: 'center',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#333',
    marginBottom: 10,
  },
  subtitle: {
    fontSize: 16,
    color: '#666',
    textAlign: 'center',
  },
});

export default MyComponent;

StyleSheet API

The StyleSheet API is the recommended way to create styles in React Native.

Creating Styles

import { StyleSheet } from 'react-native';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'white',
  },
  text: {
    fontSize: 16,
    color: 'black',
  },
});

Benefits of StyleSheet.create()

  1. Performance: Styles are compiled and optimized
  2. Validation: Props are validated in development
  3. Reusability: Styles can be reused across components
  4. Code Organization: Keeps styles organized and maintainable

StyleSheet Methods

// Create styles
const styles = StyleSheet.create({...});

// Flatten style objects
const flattenedStyle = StyleSheet.flatten([styles.base, styles.modifier]);

// Compose styles
const composedStyle = StyleSheet.compose(styles.base, styles.override);

// Get the hairline width (thinnest line possible)
const hairlineWidth = StyleSheet.hairlineWidth;

Inline Styles vs StyleSheet

Inline Styles

function InlineExample() {
  return (
    <View style={{ backgroundColor: 'red', padding: 10 }}>
      <Text style={{ color: 'white', fontSize: 16 }}>Inline Style</Text>
    </View>
  );
}

StyleSheet (Recommended)

function StyleSheetExample() {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>StyleSheet Style</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: 'red',
    padding: 10,
  },
  text: {
    color: 'white',
    fontSize: 16,
  },
});

When to Use Each

Use StyleSheet for:

  • Reusable styles
  • Complex styling
  • Performance-critical components
  • Maintainable code

Use Inline Styles for:

  • Quick prototyping
  • Dynamic styles based on props/state
  • One-off styling

Common Style Properties

Layout Properties

const layoutStyles = StyleSheet.create({
  container: {
    // Flexbox
    flex: 1,
    flexDirection: 'row', // 'row', 'column', 'row-reverse', 'column-reverse'
    justifyContent: 'center', // 'flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'space-evenly'
    alignItems: 'center', // 'flex-start', 'flex-end', 'center', 'stretch', 'baseline'
    alignSelf: 'stretch', // 'auto', 'flex-start', 'flex-end', 'center', 'stretch', 'baseline'
   
    // Dimensions
    width: 100,
    height: 200,
    minWidth: 50,
    maxWidth: 300,
    minHeight: 100,
    maxHeight: 400,
   
    // Position
    position: 'absolute', // 'relative', 'absolute'
    top: 10,
    left: 10,
    right: 10,
    bottom: 10,
    zIndex: 1,
   
    // Margins and Padding
    margin: 10,
    marginTop: 5,
    marginRight: 15,
    marginBottom: 5,
    marginLeft: 15,
    marginHorizontal: 10, // left and right
    marginVertical: 5,    // top and bottom
   
    padding: 10,
    paddingTop: 5,
    paddingRight: 15,
    paddingBottom: 5,
    paddingLeft: 15,
    paddingHorizontal: 10,
    paddingVertical: 5,
  },
});

Text Properties

const textStyles = StyleSheet.create({
  title: {
    fontSize: 24,
    fontWeight: 'bold', // 'normal', 'bold', '100'-'900'
    fontStyle: 'italic', // 'normal', 'italic'
    fontFamily: 'Arial', // Platform-specific font names
    lineHeight: 30,
    letterSpacing: 1,
    textAlign: 'center', // 'auto', 'left', 'right', 'center', 'justify'
    textAlignVertical: 'center', // Android only: 'auto', 'top', 'bottom', 'center'
    color: '#333333',
    textDecorationLine: 'underline', // 'none', 'underline', 'line-through', 'underline line-through'
    textTransform: 'uppercase', // 'none', 'uppercase', 'lowercase', 'capitalize'
  },
});

Background and Border Properties

const decorativeStyles = StyleSheet.create({
  card: {
    backgroundColor: '#ffffff',
   
    // Borders
    borderWidth: 1,
    borderColor: '#cccccc',
    borderTopWidth: 2,
    borderRightWidth: 2,
    borderBottomWidth: 2,
    borderLeftWidth: 2,
    borderRadius: 8,
    borderTopLeftRadius: 4,
    borderTopRightRadius: 4,
    borderBottomLeftRadius: 4,
    borderBottomRightRadius: 4,
   
    // Border Style
    borderStyle: 'solid', // 'solid', 'dotted', 'dashed'
   
    // Shadow (iOS)
    shadowColor: '#000000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
   
    // Elevation (Android)
    elevation: 4,
   
    // Opacity
    opacity: 0.9,
  },
});

Color and Gradients

Color Formats

const colorStyles = StyleSheet.create({
  // Named colors
  red: { backgroundColor: 'red' },
 
  // Hex colors
  blue: { backgroundColor: '#0066cc' },
 
  // RGB colors
  green: { backgroundColor: 'rgb(0, 255, 0)' },
 
  // RGBA colors
  purple: { backgroundColor: 'rgba(128, 0, 128, 0.5)' },
 
  // HSL colors (React Native 0.61+)
  orange: { backgroundColor: 'hsl(30, 100%, 50%)' },
 
  // HSLA colors
  yellow: { backgroundColor: 'hsla(60, 100%, 50%, 0.8)' },
});

Linear Gradients

For gradients, you’ll need to install react-native-linear-gradient:

npm install react-native-linear-gradient
import LinearGradient from 'react-native-linear-gradient';

function GradientExample() {
  return (
    <LinearGradient
      colors={['#ff7f00', '#ff0f7f']}
      style={styles.gradient}
      start={{ x: 0, y: 0 }}
      end={{ x: 1, y: 1 }}
    >
      <Text style={styles.gradientText}>Gradient Background</Text>
    </LinearGradient>
  );
}

const styles = StyleSheet.create({
  gradient: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  gradientText: {
    color: 'white',
    fontSize: 20,
    fontWeight: 'bold',
  },
});

Dynamic Styling

Conditional Styling

function ConditionalStyling({ isActive, type }) {
  return (
    <View style={[
      styles.button,
      isActive && styles.activeButton,
      type === 'primary' && styles.primaryButton,
    ]}>
      <Text style={[
        styles.buttonText,
        isActive && styles.activeText,
      ]}>
        {isActive ? 'Active' : 'Inactive'}
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  button: {
    padding: 10,
    borderRadius: 5,
    backgroundColor: '#f0f0f0',
  },
  activeButton: {
    backgroundColor: '#007AFF',
  },
  primaryButton: {
    backgroundColor: '#ff6b35',
  },
  buttonText: {
    color: '#333',
    textAlign: 'center',
  },
  activeText: {
    color: 'white',
    fontWeight: 'bold',
  },
});

State-Based Styling

import React, { useState } from 'react';

function StatefulButton() {
  const [pressed, setPressed] = useState(false);
 
  return (
    <Pressable
      style={[
        styles.button,
        pressed && styles.pressedButton,
      ]}
      onPressIn={() => setPressed(true)}
      onPressOut={() => setPressed(false)}
    >
      <Text style={styles.buttonText}>Press me</Text>
    </Pressable>
  );
}

const styles = StyleSheet.create({
  button: {
    backgroundColor: '#007AFF',
    padding: 15,
    borderRadius: 8,
    alignItems: 'center',
  },
  pressedButton: {
    backgroundColor: '#0051d3',
    transform: [{ scale: 0.95 }],
  },
  buttonText: {
    color: 'white',
    fontWeight: 'bold',
  },
});

Theme-Based Styling

const lightTheme = {
  background: '#ffffff',
  text: '#000000',
  primary: '#007AFF',
  secondary: '#f0f0f0',
};

const darkTheme = {
  background: '#000000',
  text: '#ffffff',
  primary: '#0A84FF',
  secondary: '#1c1c1e',
};

function ThemedComponent({ isDark }) {
  const theme = isDark ? darkTheme : lightTheme;
 
  const dynamicStyles = StyleSheet.create({
    container: {
      backgroundColor: theme.background,
      padding: 20,
    },
    title: {
      color: theme.text,
      fontSize: 24,
      fontWeight: 'bold',
    },
    button: {
      backgroundColor: theme.primary,
      padding: 10,
      borderRadius: 5,
    },
  });
 
  return (
    <View style={dynamicStyles.container}>
      <Text style={dynamicStyles.title}>Themed Component</Text>
      <TouchableOpacity style={dynamicStyles.button}>
        <Text style={{ color: 'white' }}>Themed Button</Text>
      </TouchableOpacity>
    </View>
  );
}

Responsive Design

Screen Dimensions

import { Dimensions } from 'react-native';

const screenData = Dimensions.get('screen');
const windowData = Dimensions.get('window');

function ResponsiveComponent() {
  const [screenData, setScreenData] = useState(Dimensions.get('screen'));
 
  useEffect(() => {
    const subscription = Dimensions.addEventListener('change', ({ screen }) => {
      setScreenData(screen);
    });
   
    return subscription?.remove;
  }, []);
 
  const isLandscape = screenData.width > screenData.height;
 
  return (
    <View style={[
      styles.container,
      { flexDirection: isLandscape ? 'row' : 'column' }
    ]}>
      <Text>Width: {screenData.width}</Text>
      <Text>Height: {screenData.height}</Text>
    </View>
  );
}

Percentage-Based Dimensions

const responsiveStyles = StyleSheet.create({
  container: {
    width: '100%',
    height: '50%',
  },
  halfWidth: {
    width: '50%',
  },
  quarterHeight: {
    height: '25%',
  },
});

Platform-Specific Styling

import { Platform } from 'react-native';

const platformStyles = StyleSheet.create({
  container: {
    paddingTop: Platform.OS === 'ios' ? 20 : 0,
    ...Platform.select({
      ios: {
        shadowColor: '#000',
        shadowOffset: { width: 0, height: 2 },
        shadowOpacity: 0.1,
        shadowRadius: 4,
      },
      android: {
        elevation: 4,
      },
    }),
  },
  text: {
    fontFamily: Platform.OS === 'ios' ? 'Helvetica' : 'Roboto',
  },
});

Transforms and Animations (Brief)

Basic Transforms

const transformStyles = StyleSheet.create({
  rotated: {
    transform: [{ rotate: '45deg' }],
  },
  scaled: {
    transform: [{ scale: 1.5 }],
  },
  translated: {
    transform: [
      { translateX: 50 },
      { translateY: 100 },
    ],
  },
  skewed: {
    transform: [{ skewX: '45deg' }],
  },
  combined: {
    transform: [
      { rotate: '45deg' },
      { scale: 1.2 },
      { translateX: 50 },
    ],
  },
});

Advanced Styling Techniques

Style Composition and Inheritance

// Base styles
const baseStyles = StyleSheet.create({
  button: {
    padding: 10,
    borderRadius: 5,
    alignItems: 'center',
    justifyContent: 'center',
  },
  text: {
    fontSize: 16,
    fontWeight: 'bold',
  },
});

// Extended styles
const extendedStyles = StyleSheet.create({
  primaryButton: {
    ...baseStyles.button,
    backgroundColor: '#007AFF',
  },
  secondaryButton: {
    ...baseStyles.button,
    backgroundColor: '#f0f0f0',
    borderWidth: 1,
    borderColor: '#ccc',
  },
  primaryText: {
    ...baseStyles.text,
    color: 'white',
  },
  secondaryText: {
    ...baseStyles.text,
    color: '#333',
  },
});

Style Functions

const createButtonStyle = (backgroundColor, textColor, size = 'medium') => {
  const sizeMap = {
    small: { padding: 8, fontSize: 14 },
    medium: { padding: 12, fontSize: 16 },
    large: { padding: 16, fontSize: 18 },
  };
 
  return {
    button: {
      backgroundColor,
      padding: sizeMap[size].padding,
      borderRadius: 5,
      alignItems: 'center',
    },
    text: {
      color: textColor,
      fontSize: sizeMap[size].fontSize,
      fontWeight: 'bold',
    },
  };
};

// Usage
const primaryButtonStyles = createButtonStyle('#007AFF', 'white', 'large');
const secondaryButtonStyles = createButtonStyle('#f0f0f0', '#333', 'medium');

Styled Components Pattern

// Create reusable styled components
const StyledButton = ({ children, variant = 'primary', size = 'medium', ...props }) => {
  const buttonStyle = [
    styles.baseButton,
    styles[`${variant}Button`],
    styles[`${size}Button`],
  ];
 
  const textStyle = [
    styles.baseText,
    styles[`${variant}Text`],
  ];
 
  return (
    <TouchableOpacity style={buttonStyle} {...props}>
      <Text style={textStyle}>{children}</Text>
    </TouchableOpacity>
  );
};

const styles = StyleSheet.create({
  baseButton: {
    borderRadius: 5,
    alignItems: 'center',
    justifyContent: 'center',
  },
  primaryButton: {
    backgroundColor: '#007AFF',
  },
  secondaryButton: {
    backgroundColor: '#f0f0f0',
    borderWidth: 1,
    borderColor: '#ccc',
  },
  smallButton: {
    padding: 8,
  },
  mediumButton: {
    padding: 12,
  },
  largeButton: {
    padding: 16,
  },
  baseText: {
    fontWeight: 'bold',
  },
  primaryText: {
    color: 'white',
  },
  secondaryText: {
    color: '#333',
  },
});

Performance Considerations

Optimizing Styles

// ❌ Bad: Creating styles in render
function BadComponent() {
  return (
    <View style={{ backgroundColor: 'red', padding: 10 }}>
      <Text style={{ color: 'white' }}>Bad</Text>
    </View>
  );
}

// ✅ Good: Using StyleSheet
const styles = StyleSheet.create({
  container: {
    backgroundColor: 'red',
    padding: 10,
  },
  text: {
    color: 'white',
  },
});

function GoodComponent() {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Good</Text>
    </View>
  );
}

Memoizing Dynamic Styles

import React, { useMemo } from 'react';

function OptimizedComponent({ backgroundColor, textColor }) {
  const dynamicStyles = useMemo(() => ({
    container: {
      backgroundColor,
      padding: 20,
    },
    text: {
      color: textColor,
    },
  }), [backgroundColor, textColor]);
 
  return (
    <View style={[styles.baseContainer, dynamicStyles.container]}>
      <Text style={[styles.baseText, dynamicStyles.text]}>
        Optimized Dynamic Styling
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  baseContainer: {
    borderRadius: 5,
    alignItems: 'center',
  },
  baseText: {
    fontSize: 16,
    fontWeight: 'bold',
  },
});

Common Styling Patterns

Card Component

function Card({ children, title }) {
  return (
    <View style={styles.card}>
      {title && <Text style={styles.cardTitle}>{title}</Text>}
      <View style={styles.cardContent}>
        {children}
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  card: {
    backgroundColor: 'white',
    borderRadius: 8,
    padding: 16,
    marginVertical: 8,
    marginHorizontal: 16,
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3,
  },
  cardTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    marginBottom: 12,
    color: '#333',
  },
  cardContent: {
    // Content styling
  },
});

Avatar Component

function Avatar({ source, size = 50, name }) {
  const initials = name ? name.split(' ').map(n => n[0]).join('').toUpperCase() : '';
 
  const avatarStyle = {
    width: size,
    height: size,
    borderRadius: size / 2,
  };
 
  return (
    <View style={[styles.avatarContainer, avatarStyle]}>
      {source ? (
        <Image source={source} style={[styles.avatarImage, avatarStyle]} />
      ) : (
        <Text style={[styles.avatarText, { fontSize: size * 0.4 }]}>
          {initials}
        </Text>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  avatarContainer: {
    backgroundColor: '#ccc',
    alignItems: 'center',
    justifyContent: 'center',
  },
  avatarImage: {
    resizeMode: 'cover',
  },
  avatarText: {
    color: 'white',
    fontWeight: 'bold',
  },
});

Badge Component

function Badge({ count, children, maxCount = 99 }) {
  const displayCount = count > maxCount ? `${maxCount}+` : count.toString();
 
  return (
    <View style={styles.badgeContainer}>
      {children}
      {count > 0 && (
        <View style={styles.badge}>
          <Text style={styles.badgeText}>{displayCount}</Text>
        </View>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  badgeContainer: {
    position: 'relative',
  },
  badge: {
    position: 'absolute',
    top: -8,
    right: -8,
    backgroundColor: 'red',
    borderRadius: 12,
    minWidth: 24,
    height: 24,
    alignItems: 'center',
    justifyContent: 'center',
    paddingHorizontal: 6,
  },
  badgeText: {
    color: 'white',
    fontSize: 12,
    fontWeight: 'bold',
  },
});

Debugging Styles

Inspector and Debug Tools

// Enable layout inspection in development
import { LogBox } from 'react-native';

if (__DEV__) {
  LogBox.ignoreLogs(['Warning: ...']);
}

// Add border for debugging layout
const debugStyles = StyleSheet.create({
  debugBorder: {
    borderWidth: 1,
    borderColor: 'red',
  },
});

Common Styling Issues and Solutions

// Issue: Text not showing
// Solution: Ensure parent has proper dimensions
const fixedStyles = StyleSheet.create({
  container: {
    flex: 1, // or specific height/width
  },
  text: {
    // Text will now be visible
  },
});

// Issue: Flexbox not working as expected
// Solution: Check flex container properties
const flexFixStyles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column', // Explicitly set direction
  },
  item: {
    flex: 1, // Will take up available space
  },
});

Summary

React Native styling provides a powerful and flexible way to create beautiful mobile applications. Key takeaways:

  • Use StyleSheet.create() for optimal performance and organization
  • Flexbox is the primary layout system
  • Conditional styling enables dynamic UIs
  • Platform-specific styles ensure native look and feel
  • Performance considerations are important for smooth UIs
  • Reusable components promote consistent design

Understanding these styling concepts will help you create polished, professional-looking React Native applications. In the next chapter, we’ll dive deeper into responsive layouts and Flexbox to create adaptable interfaces for different screen sizes!

Related Articles:

Scroll to Top