StyleSheet
Styles in React Native are plain JS objects with camelCase properties
(backgroundColor), not CSS. Numbers are density-independent pixels (dp),
without units; there are no px, rem or media queries, only numbers and
percentage strings. There is no cascade and no selectors: a style applies
only to the component it is passed to, and only text styles are inherited, from
a parent <Text> to a nested <Text>. StyleSheet.create groups the styles
and type-checks them; it returns the same object.
Applies to: React Native 0.88.
Details
Differences from CSS
| CSS | React Native |
|---|---|
background-color: red | backgroundColor: 'red' |
16px, 1rem, 50vw | 16 (dp), '50%'; screen size from useWindowDimensions |
| Cascade, selectors, specificity | None; combine with arrays: style={[styles.a, isActive && styles.b]} |
Inherited color, font-* everywhere | Inherited only between nested <Text> elements |
display: block by default | Everything is Flexbox, flexDirection: 'column' by default |
Media queries, :hover, pseudo-elements | None; conditions in JS |
In a style array, later entries override earlier ones; false, null and
undefined are ignored.
API
| Member | What it does |
|---|---|
StyleSheet.create(obj) | Returns obj as is; freezes it in development and gives type checking and autocompletion |
StyleSheet.flatten(arr) | Merges an array of styles into one object; later entries win |
StyleSheet.compose(a, b) | Returns [a, b]; if one is null / undefined, returns the other unchanged without a new array |
StyleSheet.hairlineWidth | The thinnest line the screen can draw, for borders and separators |
StyleSheet.absoluteFill | { position: 'absolute', top: 0, right: 0, bottom: 0, left: 0 }, for overlays |
StyleSheet.absoluteFillObject | Removed in 0.85; older code spreads it, use absoluteFill instead |
Screen Size
useWindowDimensions()returns{ width, height, scale, fontScale }and re-renders the component when the window changes (rotation, split screen).fontScaleis the system font size; layouts must grow with it, see Accessibility.Dimensions.get('window')returns the size once. Called at module level, for example insideStyleSheet.create, it never updates.- The window includes the areas under the status bar, the notch and the home indicator; keep content clear of them with insets, see Safe Area.
Styling Libraries
StyleSheet is enough for many apps. On top of it: NativeWind (Tailwind
classes), Unistyles (themes and breakpoints), styled-components/native.
See Styling approaches.
Custom Fonts
- Expo: list the files in the
expo-fontconfig plugin inapp.jsonto embed them at build time, or load them at runtime withuseFonts. - Bare React Native: add the folder to
assetsinreact-native.config.jsand runnpx react-native-asset, which copies the files into the native projects. fontFamilyis the font's PostScript name on iOS and the file name on Android. They often match (Inter-Bold), but when they do not, the font works on Android and silently falls back on iOS.- In bare apps each weight is usually a separate file referenced by its own
fontFamily(Inter-Regular,Inter-Bold). Theexpo-fontplugin can group files into one family with weights, sofontFamily: 'Inter'withfontWeight: '700'works on both platforms.
Example
import { StyleSheet, Text, View, useWindowDimensions } from 'react-native';
function Card({ selected }: { selected: boolean }) {
const { width } = useWindowDimensions();
return (
<View
style={[styles.card, selected && styles.selected, { width: width * 0.8 }]}
>
<Text style={styles.title}>
Title <Text style={styles.accent}>inherits fontSize</Text>
</Text>
<View style={styles.overlay} />
</View>
);
}
const styles = StyleSheet.create({
card: {
padding: 16,
borderBottomWidth: StyleSheet.hairlineWidth,
},
selected: { backgroundColor: '#eef' },
title: { fontSize: 18, fontFamily: 'Inter-Bold' },
accent: { color: 'royalblue' },
overlay: {
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.1)',
},
});
Follow-up Questions
- How do you do dark mode?
useColorScheme()plus your own theme object or a styling library.
Common Mistakes
- Saying
StyleSheet.createmakes styles faster than inline objects. It used to replace them with numeric IDs; now it returns the object as is. A new inline object on every render only matters for memoized children. - Reading
Dimensions.getat module level and expecting the layout to follow rotation. - Expecting
colorset on aViewto reach theTextinside it.