StyleSheet Fundamentals
Master the styling system that makes React Native apps beautiful
Table of Contents
🎯 Learning Objectives
- Understand why React Native has its own styling system
- Use
StyleSheet.create()to define optimized styles - Translate your CSS knowledge to React Native style properties
- Choose the right units and values for mobile layouts
- Combine multiple styles effectively with style arrays
- Avoid common styling mistakes web developers make
Introduction
If you've been building web applications with CSS, you've developed strong intuitions about how styling works. The good news? About 80% of that knowledge transfers directly to React Native. The challenge? That other 20% can trip you up in surprising ways if you're not prepared for it.
In Module 3, you learned about React Native's core components — View, Text, Image, and others. You saw style props sprinkled throughout our examples, but we never took a deep dive into how the styling system actually works. That changes now.
🎨 The Big Picture
React Native's StyleSheet API gives you a CSS-like syntax that compiles down to native platform styles. It's not CSS — it's a JavaScript object that describes how components should look, which then gets translated to iOS UIKit styles or Android View styles.
Think of StyleSheet as a dialect of CSS. If CSS is British English, React Native styles are American English — mostly the same, with some spelling differences and a few words that mean completely different things. Once you learn the differences, you'll move fluently between both.
What We'll Cover
This lesson establishes your styling foundation. We'll explore what StyleSheet actually does under the hood, how to use it effectively, and — critically — where it differs from web CSS. Future lessons in this module will build on these fundamentals to cover Flexbox layouts, responsive design patterns, platform-specific styling, and organizing styles at scale.
💡 Prerequisites Check
This lesson assumes you're comfortable with:
- CSS fundamentals (selectors, properties, values)
- React component basics (props, JSX)
- JavaScript objects and their syntax
- The core components from Module 3 (
View,Text, etc.)
What is StyleSheet?
Let's start with a fundamental question: why does React Native have its own styling system at all? Why not just use CSS?
The answer lies in what React Native actually is. When you write a React Native app, you're not creating a web page that runs in a browser. You're creating a truly native application where your JavaScript code controls real native UI components. iOS buttons, Android text fields, native scroll views — these are the actual building blocks of your app.
⚠️ Key Insight
There's no browser in a React Native app. No DOM, no CSS engine, no media queries. Native platforms have their own styling systems, and React Native needs to bridge the gap between the styles you write and what those platforms understand.
StyleSheet is React Native's answer to this challenge. It's an API that lets you:
- Define styles using a familiar, CSS-like syntax
- Validate styles at creation time (catching typos early)
- Optimize performance by creating style references once
- Bridge platforms by translating to native style formats
The StyleSheet Object
StyleSheet is imported from react-native and provides several useful methods and properties:
import { StyleSheet } from 'react-native';
// The main method you'll use constantly
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
},
});
// Utility for combining styles
StyleSheet.compose(style1, style2);
// Flattens an array of styles into one object
StyleSheet.flatten([style1, style2]);
// A constant representing an absolutely positioned element
// that fills its parent completely
StyleSheet.absoluteFill;
// Same as absoluteFill but as a style object you can spread
StyleSheet.absoluteFillObject;
// Returns a "hairline" width (thinnest possible line on device)
StyleSheet.hairlineWidth;
Of these, StyleSheet.create() is by far the most important. Let's explore it in depth.
How Native Platforms Handle Styles
To truly understand StyleSheet, it helps to know what happens when your styles reach the native side:
flowchart TD
subgraph JS["JavaScript Thread"]
A["StyleSheet.create()"] --> B["Style Object with IDs"]
end
subgraph Bridge["React Native Bridge"]
B --> C["Serialized Style Data"]
end
subgraph Native["Native Thread"]
C --> D{Platform?}
D -->|iOS| E["UIKit Styles
NSLayoutConstraint
CALayer properties"]
D -->|Android| F["Android View Styles
LayoutParams
Paint/Canvas"]
end
style A fill:#e1f5fe
style E fill:#f3e5f5
style F fill:#e8f5e9
When you call StyleSheet.create(), React Native doesn't just store your style objects. In production builds, it can optimize these styles and send references (IDs) across the bridge instead of full style objects every time a component renders. This is a significant performance optimization that we'll discuss more shortly.
StyleSheet.create() Explained
StyleSheet.create() takes an object where each key becomes a named style, and each value is an object of style properties. Think of it like defining CSS classes, but in JavaScript:
import { StyleSheet, View, Text } from 'react-native';
const styles = StyleSheet.create({
// Like a CSS class called ".container"
container: {
flex: 1,
backgroundColor: '#ffffff',
padding: 20,
},
// Like a CSS class called ".title"
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#1a1a1a',
marginBottom: 12,
},
// Like a CSS class called ".subtitle"
subtitle: {
fontSize: 16,
color: '#666666',
lineHeight: 24,
},
});
// Usage - reference styles by their keys
function MyComponent() {
return (
<View style={styles.container}>
<Text style={styles.title}>Welcome</Text>
<Text style={styles.subtitle}>
Getting started with StyleSheet
</Text>
</View>
);
}
Why Use StyleSheet.create()?
You might wonder: can't I just pass plain objects to the style prop? Technically, yes:
// This works, but it's not recommended
<View style={{ flex: 1, padding: 20 }}>
<Text style={{ fontSize: 24, fontWeight: 'bold' }}>
Hello
</Text>
</View>
So why bother with StyleSheet.create()? Several important reasons:
✅ Benefits of StyleSheet.create()
- Validation: Style property names are checked when styles are created. Typos like
fontSisetrigger warnings immediately. - Performance: Styles are created once and referenced by ID. Inline objects are recreated every render.
- Organization: All component styles live in one place, making them easier to find and modify.
- Reusability: Named styles can be applied to multiple elements consistently.
- Debugging: Named styles show up clearly in React DevTools.
Style Validation in Action
One of the most helpful features of StyleSheet.create() is immediate feedback on invalid styles:
// This will show a warning in development
const styles = StyleSheet.create({
broken: {
display: 'inline-block', // ❌ Not supported in RN
fontSise: 16, // ❌ Typo - should be fontSize
margin: '20px', // ❌ String units not allowed
},
});
// Correct version
const styles = StyleSheet.create({
working: {
display: 'flex', // ✅ Supported value
fontSize: 16, // ✅ Correct spelling
margin: 20, // ✅ Number (density-independent pixels)
},
});
In development mode, React Native checks your styles against known valid properties and values. This catches many common mistakes before they become visual bugs.
Placement Conventions
Where should you put your StyleSheet.create() call? The community convention is at the bottom of your component file, after the component definition:
// MyComponent.tsx
import { StyleSheet, View, Text } from 'react-native';
// Component first
export function MyComponent() {
return (
<View style={styles.container}>
<Text style={styles.text}>Hello</Text>
</View>
);
}
// Styles at the bottom
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: 18,
},
});
This pattern keeps your component's render logic at the top where it's easy to find, with styling details below. Since JavaScript hoists const declarations within modules, you can reference styles in your component even though it's defined after.
Web CSS vs React Native Styles
This is where your web CSS experience becomes both an asset and a potential source of confusion. Let's systematically compare the two systems so you know exactly what transfers and what doesn't.
Syntax Differences
The most visible difference is the syntax. CSS uses kebab-case property names and various value formats. React Native uses camelCase and JavaScript values:
Web CSS
.card {
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: 16px;
margin-bottom: 12px;
font-size: 14px;
font-weight: bold;
text-align: center;
}
React Native
const styles = StyleSheet.create({
card: {
backgroundColor: '#ffffff',
borderRadius: 8,
// Shadow is platform-specific!
padding: 16,
marginBottom: 12,
fontSize: 14,
fontWeight: 'bold',
textAlign: 'center',
},
});
Notice the patterns:
background-color→backgroundColor(camelCase)16px→16(unitless numbers)bold→'bold'(string values in quotes)box-shadow→ Different approach entirely (platform-specific)
What Transfers Directly
Many CSS concepts work almost identically in React Native. Here's what you can rely on:
✅ Works the Same (or Nearly So)
| Concept | CSS | React Native |
|---|---|---|
| Colors | #fff, rgb(), rgba() |
Same formats work! |
| Flexbox | display: flex |
Default for all Views |
| Padding/Margin | padding: 16px |
padding: 16 |
| Border radius | border-radius: 8px |
borderRadius: 8 |
| Positioning | position: absolute |
position: 'absolute' |
| Opacity | opacity: 0.5 |
opacity: 0.5 |
What's Different
Now for the gotchas. These differences catch web developers most often:
⚠️ Key Differences
| Concept | Web CSS | React Native |
|---|---|---|
| Default display | block |
flex |
| Flex direction | row (default) |
column (default) |
| Units | px, em, rem, %, vh |
Numbers (dp) and % only |
| Shorthand | margin: 10px 20px |
Must use individual props |
| Inheritance | Many properties inherit | Almost nothing inherits |
| Shadows | box-shadow |
Platform-specific APIs |
What Doesn't Exist
Some CSS features have no equivalent in React Native because they don't make sense in a native context:
❌ Not Available in React Native
- CSS Selectors: No
.class,#id,:hover,::before - Media Queries: No
@media(we have other solutions) - CSS Grid: Layout is Flexbox-only
- CSS Variables: No
var(--custom)(use JS constants) - Cascading: No stylesheet cascade or specificity
- Animation: No
@keyframes(use Animated/Reanimated) - Pseudo-elements: No
::before,::after - Float: No
float: left/right - Display types: No
inline,block,inline-block,grid
Don't panic! Each of these missing features has a React Native alternative. We'll cover responsive design without media queries in Lesson 4 of this module, and animations are covered extensively in Module 9.
Mental Model Shift
The key mental shift is this: in CSS, you're describing how elements should look, and the browser's CSS engine handles the cascade, inheritance, and specificity rules. In React Native, you're directly telling each component exactly how to style itself. There's no cascade, no inheritance to rely on, no "styles flowing down."
flowchart TB
subgraph CSS["Web CSS Model"]
direction TB
S1["Global Stylesheet"] --> C1["Cascade Rules"]
C1 --> S2["Specificity Calculation"]
S2 --> I1["Inheritance"]
I1 --> F1["Final Computed Style"]
end
subgraph RN["React Native Model"]
direction TB
ST["StyleSheet.create()"] --> D["Direct Application"]
D --> COMP["Component receives
exact style object"]
end
style CSS fill:#e3f2fd
style RN fill:#e8f5e9
This directness is actually a feature, not a bug. It makes styles predictable — what you write is exactly what you get, with no surprises from inherited styles or specificity battles.
Core Style Properties
React Native supports a subset of CSS properties, organized into categories based on what they affect. Let's survey the most important ones you'll use daily.
Layout Properties
These control how components are sized and positioned. We'll dive deep into Flexbox in the next lesson, but here's an overview:
const layoutStyles = StyleSheet.create({
container: {
// Flexbox (covered in detail next lesson)
flex: 1,
flexDirection: 'column', // 'row', 'column', 'row-reverse', 'column-reverse'
justifyContent: 'center', // main axis alignment
alignItems: 'center', // cross axis alignment
flexWrap: 'wrap', // 'nowrap', 'wrap', 'wrap-reverse'
gap: 10, // spacing between flex children
// Sizing
width: 200,
height: 100,
minWidth: 50,
maxWidth: 300,
minHeight: 50,
maxHeight: 400,
// Aspect ratio (very useful for images/videos!)
aspectRatio: 16 / 9,
// Positioning
position: 'relative', // 'relative' or 'absolute'
top: 10,
right: 10,
bottom: 10,
left: 10,
zIndex: 100,
},
});
Spacing Properties
Padding and margin work like CSS, but without shorthand:
const spacingStyles = StyleSheet.create({
card: {
// Individual sides
paddingTop: 20,
paddingRight: 16,
paddingBottom: 20,
paddingLeft: 16,
// Axis shortcuts (these DO exist!)
paddingHorizontal: 16, // = paddingLeft + paddingRight
paddingVertical: 20, // = paddingTop + paddingBottom
// All sides at once
padding: 16, // applies to all four sides
// Same pattern for margin
margin: 10,
marginHorizontal: 20,
marginVertical: 10,
marginTop: 8,
// ... etc
},
});
💡 RN-Specific Shortcuts
paddingHorizontal, paddingVertical, marginHorizontal, and marginVertical are React Native additions that don't exist in CSS. They're incredibly handy shortcuts!
Visual Properties
Colors, backgrounds, borders, and visual effects:
const visualStyles = StyleSheet.create({
card: {
// Background
backgroundColor: '#ffffff',
// Borders (no shorthand!)
borderWidth: 1,
borderColor: '#e0e0e0',
borderStyle: 'solid', // 'solid', 'dotted', 'dashed'
// Individual border sides
borderTopWidth: 2,
borderTopColor: '#2196F3',
// Border radius
borderRadius: 8,
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
// Opacity
opacity: 0.9,
// Overflow (important for borderRadius to work on children!)
overflow: 'hidden', // 'visible', 'hidden', 'scroll'
},
});
Typography Properties
These only apply to Text components (remember from Module 3 — all text must be in <Text>):
const textStyles = StyleSheet.create({
heading: {
// Font
fontSize: 24,
fontWeight: 'bold', // '100'-'900', 'normal', 'bold'
fontStyle: 'italic', // 'normal', 'italic'
fontFamily: 'System', // Platform font or custom font name
// Color
color: '#1a1a1a',
// Alignment
textAlign: 'center', // 'left', 'center', 'right', 'justify'
textAlignVertical: 'center', // Android only: 'auto', 'top', 'center', 'bottom'
// Decoration
textDecorationLine: 'underline', // 'none', 'underline', 'line-through', 'underline line-through'
textDecorationStyle: 'solid', // 'solid', 'double', 'dotted', 'dashed'
textDecorationColor: '#2196F3',
// Spacing
letterSpacing: 1.5,
lineHeight: 32,
// Transform
textTransform: 'uppercase', // 'none', 'uppercase', 'lowercase', 'capitalize'
// Shadow (text shadow, not box shadow)
textShadowColor: 'rgba(0, 0, 0, 0.25)',
textShadowOffset: { width: 1, height: 1 },
textShadowRadius: 2,
},
});
Shadow Properties (Platform Differences)
This is one area where iOS and Android diverge significantly:
const shadowStyles = StyleSheet.create({
cardWithShadow: {
backgroundColor: '#ffffff', // Required for shadows to show!
// iOS Shadow (these only work on iOS)
shadowColor: '#000000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 4,
// Android Shadow (this only works on Android)
elevation: 4,
},
});
⚠️ Shadow Reality Check
You'll often need to specify both iOS shadow properties AND Android elevation to get consistent shadows across platforms. We'll cover platform-specific styling patterns in Lesson 5 of this module.
Image-Specific Properties
When styling Image components (remember our Image lesson in Module 3?):
const imageStyles = StyleSheet.create({
photo: {
width: 200,
height: 200,
// How the image fills its container
resizeMode: 'cover', // 'cover', 'contain', 'stretch', 'center', 'repeat'
// Tint (colorize the image)
tintColor: '#2196F3', // Useful for icons!
// Border radius works on images
borderRadius: 100, // Makes circular images
},
});
Units and Values
One of the biggest adjustments for web developers is React Native's approach to units. Let's break it down completely.
No px, em, rem, vh, vw
In CSS, you have many unit options. In React Native, you have essentially two: unitless numbers and percentages.
// ❌ This won't work - string units are invalid
const wrongStyles = {
width: '200px', // Error!
fontSize: '1.5rem', // Error!
height: '100vh', // Error!
};
// ✅ This is correct
const rightStyles = StyleSheet.create({
container: {
width: 200, // Density-independent pixels
fontSize: 24, // Points
height: '100%', // Percentage (as string)
},
});
Density-Independent Pixels (dp)
When you write width: 200 in React Native, you're specifying 200 density-independent pixels (also called "points" on iOS or "dp" on Android). This is a crucial concept:
📖 Density-Independent Pixels
A density-independent pixel is a virtual unit that scales based on screen density. On a standard 1x display, 1dp = 1 physical pixel. On a 2x Retina display, 1dp = 2 physical pixels. This means width: 100 looks the same physical size across all devices, regardless of their pixel density.
This automatic scaling is why you don't need px — React Native handles the conversion to actual pixels based on device density.
Percentages
Percentages work for sizing, but they must be strings:
const percentStyles = StyleSheet.create({
halfWidth: {
width: '50%', // 50% of parent width
height: '100%', // Full parent height
},
positioned: {
position: 'absolute',
top: '10%', // 10% from top of parent
left: '5%', // 5% from left of parent
},
});
Percentages are relative to the parent container, not the screen. This differs from CSS's vh and vw units, which are relative to the viewport.
Getting Screen Dimensions
Since there's no vh or vw, how do you size things relative to the screen? Use the Dimensions API or the useWindowDimensions hook:
import {
StyleSheet,
Dimensions,
useWindowDimensions,
View
} from 'react-native';
// Method 1: Dimensions API (static, doesn't update on rotation)
const screenWidth = Dimensions.get('window').width;
const screenHeight = Dimensions.get('window').height;
const staticStyles = StyleSheet.create({
halfScreen: {
width: screenWidth / 2,
height: screenHeight * 0.3, // 30vh equivalent
},
});
// Method 2: useWindowDimensions hook (reactive, updates on rotation)
function ResponsiveComponent() {
const { width, height } = useWindowDimensions();
return (
<View
style={{
width: width * 0.8, // 80vw equivalent
height: height * 0.5 // 50vh equivalent
}}
/>
);
}
✅ Best Practice
Prefer useWindowDimensions() over Dimensions.get() in components. The hook automatically updates when the screen size changes (like device rotation), while the API call returns a static value.
Font Sizes
Font sizes are also unitless numbers, representing points:
const textStyles = StyleSheet.create({
small: { fontSize: 12 }, // 12pt
body: { fontSize: 16 }, // 16pt - good default
large: { fontSize: 20 }, // 20pt
title: { fontSize: 28 }, // 28pt
hero: { fontSize: 48 }, // 48pt
});
Unlike web where you might use rem for scalable typography, React Native fonts are fixed sizes. For accessible, user-scalable text, you'll need to implement scaling yourself or use libraries — we'll cover this in the responsive design lesson.
Inline Styles vs StyleSheet
You have two main options for applying styles in React Native: inline style objects and StyleSheet.create(). Let's understand when to use each.
Inline Styles
Inline styles are JavaScript objects passed directly to the style prop:
// Inline style - object created every render
<View style={{ flex: 1, padding: 16 }}>
<Text style={{ fontSize: 24, color: '#333' }}>
Hello World
</Text>
</View>
StyleSheet Styles
StyleSheet styles are defined once and referenced by name:
// StyleSheet - objects created once, reused
<View style={styles.container}>
<Text style={styles.title}>Hello World</Text>
</View>
const styles = StyleSheet.create({
container: { flex: 1, padding: 16 },
title: { fontSize: 24, color: '#333' },
});
When to Use Each
✅ Use StyleSheet.create() For:
- Static styles that don't change based on props or state
- Styles reused across multiple elements
- The majority of your styling needs
- Better performance and validation
💡 Use Inline Styles For:
- Dynamic values that depend on props, state, or calculations
- One-off overrides combined with StyleSheet styles
- Styles computed from
useWindowDimensions() - Animated values (which must be inline)
Real-World Pattern: Combining Both
In practice, you'll often combine StyleSheet styles with inline dynamic values:
import { StyleSheet, View, Text, useWindowDimensions } from 'react-native';
interface CardProps {
title: string;
isHighlighted: boolean;
customColor?: string;
}
function Card({ title, isHighlighted, customColor }: CardProps) {
const { width } = useWindowDimensions();
const isWideScreen = width > 600;
return (
<View
style={[
styles.card,
// Dynamic: conditional style
isHighlighted && styles.highlighted,
// Dynamic: prop-based color
customColor && { borderColor: customColor },
// Dynamic: responsive width
{ width: isWideScreen ? 400 : width - 32 },
]}
>
<Text style={styles.title}>{title}</Text>
</View>
);
}
// Static styles in StyleSheet
const styles = StyleSheet.create({
card: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
borderWidth: 2,
borderColor: '#e0e0e0',
},
highlighted: {
borderColor: '#2196F3',
backgroundColor: '#e3f2fd',
},
title: {
fontSize: 18,
fontWeight: '600',
color: '#1a1a1a',
},
});
This pattern gives you the best of both worlds: validated, optimized static styles from StyleSheet, plus the flexibility of inline styles for dynamic values.
Combining Multiple Styles
React Native's style prop accepts either a single style object or an array of styles. This is incredibly powerful for composition.
Style Arrays
When you pass an array, styles are merged left-to-right, with later styles overriding earlier ones (just like Object.assign or spread):
// Later styles override earlier ones
<View style={[styles.base, styles.override]}>
{/* If both define backgroundColor, override wins */}
</View>
const styles = StyleSheet.create({
base: {
padding: 16,
backgroundColor: '#ffffff',
borderRadius: 8,
},
override: {
backgroundColor: '#e3f2fd', // This wins!
borderRadius: 16, // This wins!
// padding stays 16 from base
},
});
Conditional Styles
Arrays shine for conditional styling. Use JavaScript's && or ternary operators:
interface ButtonProps {
label: string;
variant: 'primary' | 'secondary' | 'danger';
disabled?: boolean;
size?: 'small' | 'large';
}
function Button({ label, variant, disabled, size }: ButtonProps) {
return (
<Pressable
style={[
styles.button,
styles[variant], // Dynamic key lookup
size === 'small' && styles.small, // Conditional
size === 'large' && styles.large, // Conditional
disabled && styles.disabled, // Conditional
]}
disabled={disabled}
>
<Text style={[
styles.label,
styles[`${variant}Label`], // Dynamic key
disabled && styles.disabledLabel,
]}>
{label}
</Text>
</Pressable>
);
}
const styles = StyleSheet.create({
button: {
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8,
alignItems: 'center',
},
primary: { backgroundColor: '#2196F3' },
secondary: { backgroundColor: '#e0e0e0' },
danger: { backgroundColor: '#f44336' },
small: { paddingVertical: 8, paddingHorizontal: 16 },
large: { paddingVertical: 16, paddingHorizontal: 32 },
disabled: { opacity: 0.5 },
label: { fontSize: 16, fontWeight: '600' },
primaryLabel: { color: '#ffffff' },
secondaryLabel: { color: '#333333' },
dangerLabel: { color: '#ffffff' },
disabledLabel: { color: '#999999' },
});
⚠️ Falsy Values in Arrays
React Native gracefully handles false, null, and undefined in style arrays — they're simply ignored. This is what makes condition && styles.something work safely.
StyleSheet.compose() and StyleSheet.flatten()
React Native provides two utility methods for combining styles:
import { StyleSheet } from 'react-native';
// compose: Combines two styles (returns a new style reference)
const combined = StyleSheet.compose(styles.base, styles.variant);
// Use: <View style={combined} />
// flatten: Merges an array into a single plain object
const flattened = StyleSheet.flatten([styles.a, styles.b, styles.c]);
// Returns: { ...a, ...b, ...c } as a plain object
// flatten is useful when you need to read computed values:
const finalStyles = StyleSheet.flatten([styles.base, { backgroundColor: 'red' }]);
console.log(finalStyles.backgroundColor); // 'red'
In practice, style arrays [style1, style2] are more commonly used than these utility methods, but flatten() is handy when you need to inspect the final computed style values.
Style Inheritance Rules
This is one of the most surprising differences from web CSS. In React Native, almost no styles inherit from parent to child.
The Web CSS Model
In web CSS, many properties cascade down to descendants:
<!-- Web HTML/CSS -->
<div style="color: blue; font-family: Arial; font-size: 16px;">
This text is blue, Arial, 16px.
<span>This span inherits all of that!</span>
<p>So does this paragraph.</p>
</div>
The React Native Model
In React Native, the same structure behaves very differently:
// React Native - NO inheritance!
<View style={{ /* View styles don't affect Text children */ }}>
<Text style={styles.parentText}>
This text has explicit styles.
<Text>This nested Text inherits NOTHING by default!</Text>
</Text>
</View>
// You must explicitly style every Text
const styles = StyleSheet.create({
parentText: {
color: 'blue',
fontSize: 16,
fontFamily: 'System',
},
});
The One Exception: Nested Text
There is exactly one case where inheritance works: Text nested directly inside Text:
// Text nested in Text DOES inherit text styles
<Text style={styles.paragraph}>
This is normal text.
<Text style={styles.bold}> This inherits paragraph styles AND adds bold.</Text>
<Text style={styles.link}> This inherits AND changes color.</Text>
</Text>
const styles = StyleSheet.create({
paragraph: {
fontSize: 16,
color: '#333333',
lineHeight: 24,
},
bold: {
fontWeight: 'bold',
// Inherits fontSize, color, lineHeight from parent Text
},
link: {
color: '#2196F3', // Overrides inherited color
textDecorationLine: 'underline',
// Inherits fontSize, lineHeight from parent Text
},
});
flowchart TD
subgraph NoInherit["No Inheritance"]
V["View (styled)"] --> T1["Text"]
T1 -.->|"❌ No inheritance"| NOTE1["View styles don't
cascade to Text"]
end
subgraph Inherit["Text-to-Text Inheritance"]
T2["Text (parent styles)"] --> T3["Text (child)"]
T3 -.->|"✅ Inherits text styles"| NOTE2["fontSize, color, etc.
cascade to nested Text"]
end
style NoInherit fill:#ffebee
style Inherit fill:#e8f5e9
Practical Implication: Default Styles
Since nothing inherits, you'll often create base text styles and apply them everywhere:
// Common pattern: create reusable text style presets
const styles = StyleSheet.create({
// Base text style - apply to all Text components
text: {
fontSize: 16,
color: '#1a1a1a',
fontFamily: 'System',
},
// Variants build on or override base
heading: {
fontSize: 24,
fontWeight: 'bold',
color: '#1a1a1a',
fontFamily: 'System',
},
caption: {
fontSize: 12,
color: '#666666',
fontFamily: 'System',
},
});
// Usage: always include a text style
<Text style={styles.text}>Body text</Text>
<Text style={styles.heading}>Page Title</Text>
<Text style={styles.caption}>Small print</Text>
In Lesson 7, we'll explore patterns for organizing and scaling these base styles across large applications.
Common Pitfalls
Let's look at the mistakes web developers most commonly make when starting with React Native styling.
Pitfall 1: Using String Units
// ❌ WRONG - string units don't work
const badStyles = {
width: '200px',
fontSize: '16px',
margin: '10rem',
};
// ✅ CORRECT - use numbers
const goodStyles = StyleSheet.create({
container: {
width: 200,
fontSize: 16,
margin: 10,
},
});
Pitfall 2: Expecting CSS Shorthand
// ❌ WRONG - shorthand doesn't exist
const badStyles = {
margin: '10 20', // Nope
padding: '10px 20px 10px', // Nope
border: '1px solid black', // Nope
background: 'linear-gradient(...)', // Nope
};
// ✅ CORRECT - use individual properties
const goodStyles = StyleSheet.create({
box: {
marginVertical: 10,
marginHorizontal: 20,
// Or individually:
paddingTop: 10,
paddingRight: 20,
paddingBottom: 10,
paddingLeft: 20,
// Border must be separate
borderWidth: 1,
borderStyle: 'solid',
borderColor: 'black',
},
});
Pitfall 3: Forgetting flexDirection Default
// Web CSS: flex items default to row
// React Native: flex items default to COLUMN
// If you want horizontal layout, you MUST specify:
const styles = StyleSheet.create({
row: {
flexDirection: 'row', // Required for horizontal!
},
});
Pitfall 4: Applying Text Styles to View
// ❌ WRONG - text styles on View don't work
<View style={{ fontSize: 16, color: 'blue' }}>
<Text>This text won't be blue or 16pt!</Text>
</View>
// ✅ CORRECT - text styles go on Text
<View>
<Text style={{ fontSize: 16, color: 'blue' }}>
Now it works!
</Text>
</View>
Pitfall 5: Expecting borderRadius to Clip Children
// ❌ PROBLEM - children overflow rounded corners
<View style={{ borderRadius: 20 }}>
<Image source={...} style={{ width: '100%', height: 200 }} />
{/* Image corners stick out! */}
</View>
// ✅ SOLUTION - add overflow: 'hidden'
<View style={{ borderRadius: 20, overflow: 'hidden' }}>
<Image source={...} style={{ width: '100%', height: 200 }} />
{/* Image properly clipped to rounded corners */}
</View>
Pitfall 6: Inline Object Recreation
// ❌ SUBOPTIMAL - new object every render
function MyComponent() {
return (
<View style={{ flex: 1, padding: 16 }}>
{/* This object is recreated on every render */}
</View>
);
}
// ✅ BETTER - StyleSheet created once
function MyComponent() {
return (
<View style={styles.container}>
{/* Reference to pre-created style */}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 16 },
});
✅ Summary of Pitfalls
- Use numbers, not string units (
200not'200px') - No shorthand — specify each property individually
- Remember
flexDirection: 'column'is the default - Text styles only work on
Textcomponents - Add
overflow: 'hidden'forborderRadiusclipping - Use
StyleSheet.create()for static styles
Hands-On Exercises
Let's put your new StyleSheet knowledge into practice. Complete these exercises to solidify your understanding.
Exercise 1: Convert CSS to React Native
Convert this CSS to a React Native StyleSheet:
.profile-card {
background-color: #ffffff;
border-radius: 12px;
padding: 20px;
margin: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.profile-name {
font-size: 24px;
font-weight: bold;
color: #1a1a1a;
margin-bottom: 8px;
}
.profile-bio {
font-size: 14px;
color: #666666;
line-height: 20px;
}
Show Solution
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
profileCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 20,
margin: 16,
// iOS shadow
shadowColor: '#000000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
// Android shadow
elevation: 4,
},
profileName: {
fontSize: 24,
fontWeight: 'bold',
color: '#1a1a1a',
marginBottom: 8,
},
profileBio: {
fontSize: 14,
color: '#666666',
lineHeight: 20,
},
});
Key conversions: kebab-case → camelCase, removed px units, split box-shadow into platform-specific properties.
Exercise 2: Build a Button Component with Variants
Create a Button component that accepts variant ('primary' | 'outline'), size ('small' | 'medium' | 'large'), and disabled props. Use style arrays to combine the appropriate styles.
Show Solution
import { StyleSheet, Pressable, Text } from 'react-native';
interface ButtonProps {
title: string;
variant?: 'primary' | 'outline';
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
onPress?: () => void;
}
export function Button({
title,
variant = 'primary',
size = 'medium',
disabled = false,
onPress
}: ButtonProps) {
return (
<Pressable
style={[
styles.base,
styles[variant],
styles[size],
disabled && styles.disabled,
]}
onPress={onPress}
disabled={disabled}
>
<Text style={[
styles.text,
variant === 'outline' && styles.outlineText,
disabled && styles.disabledText,
]}>
{title}
</Text>
</Pressable>
);
}
const styles = StyleSheet.create({
base: {
borderRadius: 8,
alignItems: 'center',
justifyContent: 'center',
},
// Variants
primary: {
backgroundColor: '#2196F3',
},
outline: {
backgroundColor: 'transparent',
borderWidth: 2,
borderColor: '#2196F3',
},
// Sizes
small: {
paddingVertical: 8,
paddingHorizontal: 16,
},
medium: {
paddingVertical: 12,
paddingHorizontal: 24,
},
large: {
paddingVertical: 16,
paddingHorizontal: 32,
},
// States
disabled: {
opacity: 0.5,
},
// Text styles
text: {
fontSize: 16,
fontWeight: '600',
color: '#ffffff',
},
outlineText: {
color: '#2196F3',
},
disabledText: {
color: '#999999',
},
});
Exercise 3: Identify the Bugs
Find and fix all the bugs in this code:
const styles = StyleSheet.create({
container: {
display: 'block',
width: '100%',
padding: '16px',
},
card: {
background: '#fff',
border-radius: 8,
box-shadow: '0 2px 4px rgba(0,0,0,0.1)',
},
title: {
font-size: '24px',
font-weight: 'bold',
},
});
function Card() {
return (
<View style={styles.card}>
<Text>Hello World</Text>
</View>
);
}
Show Solution
const styles = StyleSheet.create({
container: {
// ❌ display: 'block' → ✅ removed (View uses flex by default)
// ❌ width: '100%' → ✅ width: '100%' (percentages are strings, this is OK)
width: '100%',
// ❌ padding: '16px' → ✅ padding: 16
padding: 16,
},
card: {
// ❌ background → ✅ backgroundColor
backgroundColor: '#fff',
// ❌ border-radius → ✅ borderRadius (camelCase!)
borderRadius: 8,
// ❌ box-shadow doesn't exist → ✅ platform-specific shadow
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 2,
},
title: {
// ❌ font-size → ✅ fontSize
// ❌ '24px' → ✅ 24
fontSize: 24,
// ❌ font-weight → ✅ fontWeight
fontWeight: 'bold',
},
});
function Card() {
return (
<View style={styles.card}>
{/* Note: Text has no style, so default system font/size/color */}
<Text style={styles.title}>Hello World</Text>
</View>
);
}
Bugs found: display: 'block' invalid, kebab-case properties, string units, background should be backgroundColor, box-shadow doesn't exist, Text component missing style.
Summary
You've now built a solid foundation in React Native styling. Let's recap the key takeaways:
🎯 Key Concepts
- StyleSheet.create() validates and optimizes your styles — use it for all static styles
- No CSS engine — React Native translates JS style objects to native platform styles
- camelCase properties, unitless numbers, and no shorthand
- Flexbox is default and
flexDirectiondefaults to'column' - Style arrays let you combine and conditionally apply styles
- No inheritance except for nested
Textcomponents - Platform differences exist, especially for shadows
Coming Up Next
In the next lesson, we'll dive deep into Flexbox in React Native. You'll learn how the default flex layout system works, master alignment and distribution, and build common layout patterns used in real mobile apps. The Flexbox knowledge you have from web development will serve you well, but there are some important differences we'll cover.
Quick Reference
// StyleSheet fundamentals cheat sheet
import { StyleSheet, View, Text } from 'react-native';
// 1. Create styles (at bottom of file)
const styles = StyleSheet.create({
container: {
flex: 1, // Takes full available space
backgroundColor: '#fff', // camelCase!
padding: 16, // Unitless number (dp)
},
text: {
fontSize: 16, // On Text only
color: '#333', // On Text only
fontWeight: 'bold', // String values
},
});
// 2. Apply styles
<View style={styles.container}>
<Text style={styles.text}>Hello</Text>
</View>
// 3. Combine styles (array)
<View style={[styles.base, styles.variant, { padding: 20 }]} />
// 4. Conditional styles
<View style={[styles.box, isActive && styles.active]} />
// 5. Dynamic inline (for calculated values)
<View style={{ width: screenWidth * 0.8 }} />