Flexbox is the entire layout system. There is no grid, no float, no
position: fixed, and no media queries. Everything on a React Native screen is
positioned by flexbox, which is a much smaller thing to learn than CSS layout — once you know the
handful of ways it differs.
Three defaults that are not the web's
These bite constantly when porting a design, and each is one word.
flexDirection defaults to 'column', not
'row'. Phones are tall; stacking is the common case. Every row in your app says so
explicitly.
alignItems defaults to 'stretch'. Children fill the
cross axis unless told otherwise — so a View in a column is full width by default,
which is usually what you want and occasionally very confusing.
Everything is display: flex already. There is no
display property to set, and no inline. A View is a flex
container, always.
The row you will write a hundred times
footer: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },Label on the left, value on the right, vertically centred. That single line is most of the UI in most apps — price rows, list headers, a title with a badge beside it.
The axes work exactly as on the web: justifyContent along the main axis,
alignItems across it. Change flexDirection and the two swap meaning, which
is the one genuinely confusing part of flexbox anywhere.
flex: 1
flex: 1 means "take the remaining space". It is how you make a screen fill the
window, how a scroll area expands, and how one item in a row pushes another to the edge.
pressable: { flex: 1 },
pressed: { opacity: 0.8 },
card: { padding: 0, overflow: 'hidden', flex: 1 },Two of those three are flex: 1 for the same reason: the cards sit in a
two-column grid, and without it a card with a short description would be shorter than its neighbour
and the grid would go ragged.
⚠️ The classic mistake is expecting flex: 1 on a child to work when its
parent has no height. Space can only be distributed if there is space; a column of
unsized parents distributes nothing, and the child collapses to zero. When something has vanished,
walk up the tree — the missing flex: 1 is almost always above the element you are
looking at.
gap, and why you should use it
chipRow: { flexDirection: 'row', flexWrap: 'wrap', gap: theme.spacing.sm },gap spaces children without touching them. The alternative — a
marginRight on every child and then a rule to strip it from the last one — is a
recurring source of off-by-one bugs, and it stops working the moment the row wraps.
flexWrap: 'wrap' is what lets that row of topping chips flow onto as many lines as it
needs. With gap, wrapped rows are spaced correctly in both directions for free.
Text has no overflow
There is no text-overflow: ellipsis, and no overflow: hidden to fall
back on for text. Long text simply pushes the layout around. The fix is a prop on the
Text itself:
<View style={styles.footer}>
{cheapest !== null ? (
<Text variant="caption" tone="muted">
from{' '}
<Text variant="bodyStrong" tone="primary">
{formatMoney(cheapest)}
</Text>
</Text>
) : (
<Text variant="caption" tone="subtle">
Unavailable
</Text>
)}numberOfLines={2} on the description above this truncates with an ellipsis and,
more importantly, stops a wordy product from pushing the price row down and breaking the grid
alignment. In a list of cards, that prop is the difference between a tidy grid and a ragged one.
Notice the nested Text too. On the web you would use a <span>;
here a Text inside a Text flows inline with it, which is the only way to
style part of a sentence differently. The {' '} is a deliberate space — JSX collapses
whitespace across lines, so it has to be written.
Percentages, and the absence of media queries
Percentage strings work — width: '50%', maxHeight: '90%' — and are
relative to the parent, as on the web. What does not exist is a media query. Responsive layout is
done in JavaScript, with useWindowDimensions, and it re-runs when the device rotates.
Lesson 17 covers it.
In practice most phone layouts need far less of this than a website. A single column that flexes covers the majority; tablets and landscape are where the dimension hook earns its keep.
Debugging a layout
The fastest trick in React Native, unchanged in a decade: give the suspect view a garish background colour and see what it actually occupies. Layout bugs here are almost always "this box is not the size I assumed", and colouring it in answers that in seconds where reading the styles does not.
The dev menu's element inspector does the same job more politely, and React DevTools shows the computed layout for a selected node.
What is next
Safe Areas, Notches and the Keyboard — the part of the screen you do not actually own.