From the author of the Telegram channel REACT NATIVE HUB
Join a growing community of React Native Devs! 👆
When I first started building React Native apps, I thought styling would be the easy part. After all, it’s just CSS-like properties, right? I couldn’t have been more wrong. Over the years of shipping production apps, I’ve learned that thoughtful styling architecture is what separates amateur projects from professional, maintainable applications.
Today, I want to share the styling patterns and practices that have transformed how I approach React Native development — techniques that will save you hours of debugging and make your apps a joy to maintain.
The Foundation: Building a Design System That Scales
Why Most React Native Apps Look Inconsistent
The biggest mistake I see developers make is hardcoding values throughout their components. You’ll see things like:
// ❌ This leads to inconsistency and maintenance nightmares
const styles = StyleSheet.create({
header: {
backgroundColor: '#3498db',
padding: 16,
fontSize: 18,
},
button: {
backgroundColor: '#3490db', // Oops, slightly different blue
padding: 15, // Almost the same padding
fontSize: 16,
},
});
These small inconsistencies compound quickly, creating apps that feel unpolished and amateur.
Creating Your Design Token System
Instead, establish a centralized theme system from day one:
// theme.js - Your single source of truth
export const COLORS = {
primary: '#3498db',
primaryDark: '#2980b9',
secondary: '#2ecc71',
background: '#f9f9f9',
surface: '#ffffff',
text: '#333333',
textSecondary: '#666666',
border: '#dee2e6',
error: '#e74c3c',
success: '#27ae60',
};
export const SPACING = {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
xxl: 48,
};
export const TYPOGRAPHY = {
sizes: {
small: 12,
body: 16,
heading: 20,
title: "24,"
},
weights: {
regular: '400',
medium: '500',
bold: '700',
},
};
Now every component uses these constants:
import { COLORS, SPACING, TYPOGRAPHY } from './theme';
const styles = StyleSheet.create({
container: {
backgroundColor: COLORS.background,
padding: SPACING.md,
},
title: "{"
fontSize: TYPOGRAPHY.sizes.title,
fontWeight: TYPOGRAPHY.weights.bold,
color: COLORS.text,
marginBottom: SPACING.sm,
},
});
This approach has saved me countless hours. When the design team asks to adjust the primary color or increase spacing, I change one value and it updates everywhere.
Building Reusable Component Libraries
The Problem with Inline Styling
I used to write components like this:
// ❌ Repetitive and hard to maintain
function UserProfile() {
return (
<View>
<Text style={{ fontSize: 24, fontWeight: 'bold', color: '#333' }}>
John Doe
</Text>
<Text style={{ fontSize: 16, color: '#666' }}>
Software Developer
</Text>
</View>
);
}
Creating Semantic Component APIs
Now I build reusable text components:
// components/Typography.js
import React from 'react';
import { Text, StyleSheet } from 'react-native';
import { COLORS, TYPOGRAPHY } from '../theme';
export const Title = ({ children, style, ...props }) => (
<Text style={[styles.title, style]} {...props}>
{children}
</Text>
);
export const Body = ({ children, style, variant = 'primary', ...props }) => (
<Text style={[styles.body, styles[variant], style]} {...props}>
{children}
</Text>
);
export const Caption = ({ children, style, ...props }) => (
<Text style={[styles.caption, style]} {...props}>
{children}
</Text>
);
const styles = StyleSheet.create({
title: "{"
fontSize: TYPOGRAPHY.sizes.title,
fontWeight: TYPOGRAPHY.weights.bold,
color: COLORS.text,
},
body: {
fontSize: TYPOGRAPHY.sizes.body,
fontWeight: TYPOGRAPHY.weights.regular,
},
primary: {
color: COLORS.text,
},
secondary: {
color: COLORS.textSecondary,
},
caption: {
fontSize: TYPOGRAPHY.sizes.small,
color: COLORS.textSecondary,
},
});
Now my components are clean and semantic:
function UserProfile() {
return (
<View>
<Title>John Doe</Title>
<Body variant="secondary">Software Developer</Body>
</View>
);
}
Flexbox Mastery: Layouts That Actually Work
Common Flexbox Patterns I Use Daily
After building dozens of apps, I’ve identified the flexbox patterns I reach for constantly:
// styles/common.js
export const flexPatterns = StyleSheet.create({
// Container patterns
row: {
flexDirection: 'row',
},
column: {
flexDirection: 'column',
},
center: {
justifyContent: 'center',
alignItems: 'center',
},
spaceBetween: {
justifyContent: 'space-between',
},
// Child patterns
flex1: {
flex: 1,
},
alignSelfCenter: {
alignSelf: 'center',
},
});
Real-World Layout Example
Here’s how I structure a typical product card:
import { flexPatterns } from '../styles/common';
function ProductCard({ product, onAddToCart }) {
return (
<View style={[styles.card, flexPatterns.row]}>
<Image source={{ uri: product.image }} style={styles.image} />
<View style={[flexPatterns.flex1, flexPatterns.column]}>
<Title>{product.name}</Title>
<Body variant="secondary" numberOfLines={2}>
{product.description}
</Body>
<View style={[flexPatterns.row, flexPatterns.spaceBetween, styles.footer]}>
<Text style={styles.price}>${product.price}</Text>
<Button onPress={() => onAddToCart(product)}>
Add to Cart
</Button>
</View>
</View>
</View>
);
}
Responsive Design: One Codebase, All Screen Sizes
The Hook That Changed Everything
I created this custom hook that I now use in every project:
// hooks/useResponsive.js
import { useState, useEffect } from 'react';
import { Dimensions } from 'react-native';
export const useResponsive = () => {
const [dimensions, setDimensions] = useState(Dimensions.get('window'));
useEffect(() => {
const subscription = Dimensions.addEventListener('change', ({ window }) => {
setDimensions(window);
});
return () => subscription?.remove();
}, []);
const { width, height } = dimensions;
const isTablet = width > 768;
const isLandscape = width > height;
return { width, height, isTablet, isLandscape };
};
Building Adaptive Components
Now I can create components that respond intelligently to screen size:
function ProductGrid({ products }) {
const { isTablet } = useResponsive();
const styles = StyleSheet.create({
grid: {
flexDirection: 'row',
flexWrap: 'wrap',
},
item: {
width: isTablet ? '33.33%' : '50%',
padding: SPACING.sm,
},
});
return (
<View style={styles.grid}>
{products.map(product => (
<View key={product.id} style={styles.item}>
<ProductCard product={product} />
</View>
))}
</View>
);
}
Platform-Specific Styling Done Right
The Shadow Problem
One of the most frustrating aspects of React Native is handling shadows. iOS and Android require completely different approaches:
// utils/shadows.js
import { Platform } from 'react-native';
export const createShadow = (elevation = 2) => {
return Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: elevation },
shadowOpacity: 0.2,
shadowRadius: elevation * 2,
},
android: {
elevation,
},
});
};
Using Platform-Specific Styles
Now I can apply consistent shadows across platforms:
const styles = StyleSheet.create({
card: {
backgroundColor: COLORS.surface,
borderRadius: 8,
padding: SPACING.md,
...createShadow(4),
},
});
Dynamic Styling and Theme Support
Building a Theme Context
Modern apps need to support dark mode and theme switching:
// context/ThemeContext.js
import React, { createContext, useContext, useState } from 'react';
import { Appearance } from 'react-native';
const ThemeContext = createContext();
export const ThemeProvider = ({ children }) => {
const [isDark, setIsDark] = useState(
Appearance.getColorScheme() === 'dark'
);
const theme = isDark ? darkTheme : lightTheme;
const toggleTheme = () => setIsDark(!isDark);
return (
<ThemeContext.Provider value={{ theme, isDark, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
export const useTheme = () => useContext(ThemeContext);
Theme-Aware Components
Components can now automatically adapt to theme changes:
function ThemedCard({ children }) {
const { theme } = useTheme();
const styles = StyleSheet.create({
card: {
backgroundColor: theme.colors.surface,
borderColor: theme.colors.border,
borderWidth: StyleSheet.hairlineWidth,
borderRadius: 8,
padding: theme.spacing.md,
},
});
return <View style={styles.card}>{children}</View>;
}
Key Takeaways for Professional React Native Styling
- Start with a design system — Define your colors, spacing, and typography constants before writing any component styles
- Build reusable components — Create semantic APIs that encapsulate styling decisions
- Master flexbox patterns — Lear
- n the common layout patterns and reuse them consistently
- Plan for responsive design — Use hooks and utilities to adapt to different screen sizes
- Handle platform differences gracefully — Create utilities that abstract platform-specific styling
- Support theming from day one — Modern apps need dark mode and theme flexibility
The time you invest in setting up proper styling architecture will pay dividends throughout your project’s lifecycle. When design changes come (and they always do), you’ll be able to implement them quickly and consistently across your entire app.
These patterns have transformed how I build React Native apps. They’ve made my code more maintainable, my apps more consistent, and my development process significantly more enjoyable. Give them a try in your next project — I think you’ll find them as valuable as I have.
About me: My name is Arsen, and I am a react native developer and owner of the TG channel 👇
🔗 Join TG community for React Native Devs: REACT NATIVE HUB