StyleSheet and the style Prop
Styles are JavaScript objects passed through the style prop, merged from arrays, with no cascade and no inheritance.
const styles = StyleSheet.create({ card: { padding: 16 } })A style in React Native is a plain JavaScript object. Keys are camelCased property names, values are numbers or strings. There is no stylesheet file and no selector language. A component is styled only by what you hand to its style prop.
StyleSheet.create takes an object of named styles and gives it back. The React Native docs describe it as an identity function, so its value is type checking: each key is checked against the real native style properties, and a typo becomes a compile error instead of a silently ignored rule. Define styles once at module scope rather than rebuilding objects on every render.
Arrays and overrides
The style prop also accepts an array. Entries merge left to right, so a later entry wins on any property it sets. Entries that are false, null or undefined are skipped. That is why conditional styling is written [styles.card, active && styles.active]. The longhand of that shorthand is StyleSheet.compose(styles.card, active ? styles.active : null), and the two are the same thing; compose only avoids allocating an array when one side is falsy. StyleSheet.flatten collapses an array into one object whose values you can read.
Where it stops looking like CSS
There is no cascade and no inheritance. A color or fontSize set on a View does nothing; text styles pass down only from a Text to the Text nested inside it. Multi-value shorthands do not exist: margin: '0 auto' and padding: '8px 16px' are invalid. Units are omitted, because a bare number means density-independent pixels. StyleSheet.hairlineWidth gives the thinnest line the current screen can draw, and StyleSheet.absoluteFill is a ready-made overlay object.
Example
import { StyleSheet, Text, View } from 'react-native';
type CardProps = {
title: string;
body: string;
highlighted?: boolean;
};
function Card({ title, body, highlighted = false }: CardProps) {
// Array form: entries merge left to right, falsy entries are skipped.
return (
<View style={[styles.card, highlighted && styles.cardHighlighted]}>
<Text style={styles.title}>{title}</Text>
<Text style={styles.body}>{body}</Text>
</View>
);
}
export default function StyleSheetDemo() {
return (
<View style={styles.screen}>
<Card title="Plain" body="Default border." />
<Card title="Highlighted" body="Border overridden." highlighted />
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16, gap: 12, backgroundColor: '#ffffff' },
card: {
padding: 16,
borderRadius: 12,
borderWidth: StyleSheet.hairlineWidth, // thinnest visible line
borderColor: '#c7c7cc',
},
cardHighlighted: { borderWidth: 2, borderColor: '#0a7ea4' },
// These two live on Text, not on the card: a View ignores text styles.
title: { fontSize: 17, fontWeight: '600', color: '#1c1c1e' },
body: { fontSize: 15, color: '#3c3c43' },
});When to use it
- Give a reusable Card or Button a base style plus a variant style chosen at render time.
- Draw a one-pixel divider that stays crisp on every screen density with StyleSheet.hairlineWidth.
- Let a shared component accept a style prop so screens can override its padding or colour.
- Overlay a translucent layer on an image without hardcoding the four edge offsets.
More examples
absoluteFill as the shorthand for four offsets
A 160dp square with a grey block, a translucent layer covering it exactly, and a caption on top. styles.fillLonghand is written out purely to show what StyleSheet.absoluteFill contains; the two are interchangeable. Reach for it for any overlay, loading veil or tap-catching layer that must cover its parent.
import { StyleSheet, Text, View } from 'react-native';
export default function Scrim() {
return (
<View style={styles.container}>
<View style={styles.photo} />
{/* StyleSheet.absoluteFill is the same object as styles.fillLonghand */}
<View style={[StyleSheet.absoluteFill, styles.dim]} />
<Text style={styles.label}>Sold out</Text>
</View>
);
}
const styles = StyleSheet.create({
container: { width: 160, height: 160, borderRadius: 12, overflow: 'hidden' },
photo: { flex: 1, backgroundColor: '#8e8e93' },
fillLonghand: {
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
},
dim: { backgroundColor: 'rgba(0, 0, 0, 0.45)' },
label: { position: 'absolute', bottom: 8, left: 8, color: '#ffffff' },
});Accepting a style prop and reading the merged result
Renders the price, and tightens letter spacing only when the caller has pushed the font size to 24 or above. StyleProp<TextStyle> is the type for anything the style prop accepts: an object, an array, or nothing. Use this pattern for shared components that must stay restyleable, and whenever a decision depends on the style actually in force rather than the one you wrote.
import { StyleSheet, Text, type StyleProp, type TextStyle } from 'react-native';
type PriceProps = {
amount: number;
style?: StyleProp<TextStyle>; // callers may override anything
};
export default function Price({ amount, style }: PriceProps) {
// flatten turns the array (and any nested arrays) into one plain object.
const merged = StyleSheet.flatten([styles.price, style]);
const isLarge = (merged.fontSize ?? 0) >= 24;
return (
<Text style={[merged, isLarge && styles.tight]}>
{`$${amount.toFixed(2)}`}
</Text>
);
}
const styles = StyleSheet.create({
price: { fontSize: 16, fontWeight: '600', color: '#1c1c1e' },
tight: { letterSpacing: -0.5 },
});
Discussion