Chapter 04 of 09Part 2 · Layout
Flexbox vs grid: laying out a page
Flexbox lines things up in a row or a column, grid draws rows and columns at once. Learn when to use each.
11 minBy Zuzo StudioUpdated
In this chapter you will
- tell when a layout is a flexbox job and when it is a grid job
- align and space items with justify-content, align-items and gap
- build columns that adapt to the screen with repeat, minmax and auto-fit
Before you startThis builds on The cascade and the CSS box model.
In the last chapter every element was a box, and boxes stacked one under the other. That is normal flow, and it gets you surprisingly far. The moment you want two things side by side, though, you need a layout method. Modern CSS gives you two that cover almost every case, and the question beginners ask first is flexbox vs grid: which one, and when. The short answer is that they solve different problems, and most real pages use both. This chapter shows you how each one thinks, so you can pick without guessing.
01Flexbox vs grid: one direction or two
The clearest way to tell them apart comes from MDN's guide on how grid relates to other layout methods: flexbox was designed for layout in one dimension, a row or a column, and grid was designed for two dimensions, rows and columns at the same time.
That sounds abstract, so here is what it means in practice:
- Flexbox lines items up along one line. It looks at the items first and asks how much room each one needs, then shares out what is left. A row of buttons, a header with a logo on the left and links on the right, a tag list that wraps: these are flexbox jobs.
- Grid draws the lines first. You define columns (and rows, if you need them) on the parent, and the children drop into the cells. A set of cards that must line up in columns, a page with a sidebar, a gallery: these are grid jobs.
A useful way to say it: flexbox works from the content out, grid works from the layout in. If you care that items line up with each other across rows, you want grid. If you only care that items in one line sit nicely together, flexbox is usually simpler.
Both are switched on the same way. You set display on the parent, which becomes the container, and its direct children become items. Grandchildren are not affected, which catches a lot of people out.
.site-header { display: flex; }
.services { display: grid; }
02How flexbox thinks
A flex container has two axes. The main axis runs in the direction of flex-direction: left to right for row (the default in English), top to bottom for column. The cross axis runs at right angles to it. Almost every flexbox property is about one of those two axes, and once that clicks the property names stop feeling random.
justify-contentplaces items along the main axis:flex-start,center,space-betweenand so on.align-itemsplaces items along the cross axis. The default,stretch, makes every item as tall as the tallest one in a row, which is why flex columns often look equal height without you asking.flex-wrap: wraplets items move onto a new line when they run out of room. Without it, flexbox squeezes everything onto one line.gapsets the space between items, without the margin tricks people used to need.
MDN's basic concepts of flexbox adds an important detail about wrapping: each wrapped line behaves like its own flex container. Items on the second line do not line up with items on the first. That is the one dimensional model showing, and it is the most common reason a "flexbox grid" of cards looks ragged on its last row.
Here is the classic header: logo on one side, navigation on the other, both centred vertically.
.site-header {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1rem;
}
How items share space
Each flex item has three sizing properties, usually written together with the shorthand flex. flex-grow says whether an item may take spare room, flex-shrink whether it may give room up, and flex-basis what size it starts from. Two values cover most cases:
flex: 1means "share the spare room equally with the other items that also haveflex: 1".flex: nonemeans "stay exactly the size of your content".
A search bar that fills the space beside a fixed button is flex: 1 on the input and nothing on the button.
03How grid thinks
A grid container is defined by its tracks: the columns and rows. You describe the columns with grid-template-columns, and grid creates rows as it needs them.
Grid brings its own unit, fr, which MDN's basic concepts of grid layout describes as a fraction of the available space in the grid container. 1fr 1fr 1fr gives three equal columns. 2fr 1fr gives a main column twice as wide as the one beside it. Because fr shares out space after gaps are taken away, you never have to do percentage maths with gutters again.
.services {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
}
repeat(3, 1fr) is just a short way of writing 1fr 1fr 1fr.
Columns that decide for themselves
The most useful line of grid you will learn is this one:
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
Read it from the inside out. minmax(16rem, 1fr) says each column is at least 16rem wide and at most one share of the space. auto-fit says "fit in as many of those columns as the container allows". On a phone you get one column, on a tablet two, on a wide screen three or four, and you have not written a single media query. web.dev's grid module walks through this pattern and the small difference between auto-fit and auto-fill: with only a few items, auto-fill keeps empty tracks in place, while auto-fit collapses them so the items stretch.
Switch between Flex and Grid in the playground below, change the settings, and watch the CSS it generates update as you go. Try flex-wrap with five items, then the same items with repeat(auto-fit, minmax(8rem, 1fr)), and look at how the last row behaves in each.
Flexbox lays items out along one direction and lets them grow or shrink; grid places them on rows and columns you define.
04Choosing between them
Most decisions come down to the questions in this table.
| The job | Reach for | Why |
|---|---|---|
| A row of links or buttons | Flexbox | One line, items sized by their content |
| Logo left, menu right | Flexbox | justify-content: space-between does it in one line |
| Centring one thing in a box | Either | display: grid; place-items: center is the shortest |
| Cards that line up in columns | Grid | Rows line up with each other |
| A page with a sidebar | Grid | You are defining columns, not arranging content |
| Tags or chips that wrap | Flexbox | Each line can be a different length, and that is fine |
They also nest without any trouble. A typical section is a grid of cards, each card is a flex column (image, heading, text, link pushed to the bottom with margin-top: auto), and the buttons inside it are a flex row. Pick per box, not per page.
If you find yourself setting the same columns and gaps on every page, that is a sign they belong in shared layout rules, which is a large part of what a design system gives a team.
05Keep the visual order and the source order the same
Both flexbox and grid can move things around on screen without changing the HTML. Flexbox has order and flex-direction: row-reverse. Grid lets you place any item in any cell. That power is easy to misuse.
People who use a keyboard move through a page in the order of the HTML, and screen readers read it in that order too. If the HTML says "price, then button" but the layout shows the button first, a keyboard user's focus jumps around the screen in a way that makes no sense. The web.dev grid module warns about the same thing with grid-auto-flow: dense, which fills gaps with later items and can leave the display disconnected from the logical order.
The rule we follow: write the HTML in the order that makes sense to read, then use flexbox and grid to arrange that order, not to replace it. If a design only works with the items reordered, change the HTML. You saw why that order matters in the semantic HTML chapter, and we come back to keyboard use in accessibility basics.
06Your page so far, laid out
So far your project page stacks everything in a single column, which is fine on a phone and a little sad on a laptop. Two small rules fix most of it.
header { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 1rem; }
nav ul { display: flex; gap: 1.5rem; list-style: none; padding: 0; }
.services { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 1.5rem; }
Your class names may differ from these; use whatever you set up in the earlier chapters. In the next chapter we make the rest of the page respond to the screen it is on: the viewport, media queries, text that scales and images that fit.
Check your understanding
3 questions
Key takeaways
- Flexbox lays items out in one dimension, a row or a column; grid lays them out in rows and columns at the same time.
- Flexbox works from the content out, grid works from the layout in, and most pages use both, chosen box by box.
- In flexbox, justify-content works along the main axis and align-items along the cross axis.
- repeat(auto-fit, minmax(16rem, 1fr)) gives you responsive columns without a single media query.
- Keep the HTML in reading order and use layout to arrange it, not to reorder it.
Sources
- Relationship of grid layout to other layout methodsMDN Web Docs, checked
- Basic concepts of flexboxMDN Web Docs, checked
- Basic concepts of grid layoutMDN Web Docs, checked
- Gridweb.dev, checked