CSS Flexbox Tutorial: Lay Out Rows and Columns With Flex
Web DevelopmentLesson 3 of 45 min read
What you'll learn
- Distinguish the main axis from the cross axis and set flex-direction
- Align and distribute items with justify-content, align-items and gap
- Calculate how flex-grow, flex-shrink and flex-basis share space
- Fix common flexbox problems such as overflowing text and uneven wrapping
On this page
Think of a podcast app's top bar: the app name on the left, three links in the middle, and a Subscribe button pinned to the far right, all vertically centred. Before flexbox, this was a tangle of floats and fixed widths. With flexbox it is five lines of CSS. The trick is to learn one idea first, which axis you are working on, and everything else follows.
Turn it on and meet the two axes
Flexbox is a layout mode for a container and its direct children. Setting display: flex on the container turns each child into a flex item. Items now line up along the main axis (a row by default, left to right), and the cross axis runs perpendicular to it. Properties that distribute items along the main axis are different from the ones that align them on the cross axis, and that difference is what confuses people. Change flex-direction to column and the axes swap, so justify-content suddenly works vertically.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Podcast app top bar</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; font-family: system-ui, sans-serif; }
.topbar {
display: flex;
align-items: center; /* cross axis: vertical centring */
gap: 1.5rem; /* space between items */
padding: 0.75rem 1.25rem;
background: #14213d;
color: white;
}
.brand { font-weight: 700; font-size: 1.25rem; margin-right: auto; } /* absorbs free space */
.topbar a { color: white; text-decoration: none; }
.subscribe { background: #fca311; color: #14213d; padding: 0.5rem 1rem; border-radius: 4px; }
</style>
</head>
<body>
<header class="topbar">
<span class="brand">Tidepool FM</span>
<a href="/episodes">Episodes</a>
<a href="/guests">Guests</a>
<a href="/about">About</a>
<a class="subscribe" href="/subscribe">Subscribe</a>
</header>
</body>
</html>A full-width dark navy bar appears at the top of the page. "Tidepool FM" is bold at the far left. The three white links and the orange Subscribe button sit together at the far right, each 24 pixels apart, and everything is vertically centred even though the button is taller than the links. The magic is margin-right: auto on the brand: an auto margin on a flex item takes all the leftover space on that side, which pushes the remaining items to the opposite end. It is the easiest way to split a toolbar into a left group and a right group.
Container properties versus item properties
| Property | Set on | What it does |
|---|---|---|
display: flex | Container | Creates the flex formatting context |
flex-direction | Container | Row, column or reversed versions; sets the main axis |
justify-content | Container | Distributes items along the main axis (flex-start, center, space-between, space-around, space-evenly) |
align-items | Container | Aligns items on the cross axis (stretch by default, center, flex-start, baseline) |
gap | Container | Space between items, without outer edges |
flex-wrap | Container | Let items wrap onto new lines instead of shrinking |
flex | Item | Shorthand for grow, shrink and basis |
align-self | Item | Overrides align-items for one item |
How flex sizes items: grow, shrink, basis
Each item starts from a flex-basis: its preferred size along the main axis (by default its width or content size). If there is free space left over, items with a flex-grow above 0 divide it in proportion to their grow values. If there is not enough room, items with flex-shrink above 0 give up space in proportion to their basis. The shorthand flex: 1 means 1 1 0%: grow, shrink, and start from zero.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Flex sharing</title>
<style>
* { box-sizing: border-box; }
.row { display: flex; width: 600px; border: 2px solid #333; }
.row > div { padding: 1rem 0; color: white; text-align: center; }
.a { flex: 0 0 150px; background: #264653; } /* fixed 150px */
.b { flex: 1; background: #2a9d8f; } /* 1 share */
.c { flex: 2; background: #e76f51; } /* 2 shares */
</style>
</head>
<body>
<div class="row">
<div class="a">Fixed</div>
<div class="b">One</div>
<div class="c">Two</div>
</div>
</body>
</html>The items have vertical padding only, so horizontal sizes come purely from flex. With box-sizing: border-box and the 2-pixel borders, the inner width is 600 - 4 = 596 pixels. Item A takes exactly 150. The remaining 446 pixels are split in a 1:2 ratio, so B gets about 148.7 pixels and C about 297.3, and the three coloured blocks fill the row with no gaps. If you remove the border from the row, the numbers become a clean 150, 150 and 300. Change the container to width: 300px and A refuses to shrink (0 shrink), so B and C squeeze into the 146 pixels that remain (about 49 and 97 pixels).
Wrapping cards and centring things
To make a row of cards that wraps onto new lines on small screens, add flex-wrap: wrap and give each card a basis plus permission to grow, such as flex: 1 1 14rem. Cards then try to be at least 14 rem wide, and as many as fit go on each line. Note that the last line's items will stretch wider than the ones above, because flexbox lays out one line at a time. When you need aligned columns across lines, CSS Grid, covered in the next lesson, is the better tool.
/* 1. Perfectly centred content */
.hero { display: flex; justify-content: center; align-items: center; min-height: 60vh; }
/* 2. Wrapping cards with a minimum width */
.cards { display: flex; flex-wrap: wrap; gap: 1rem; }
.cards > article { flex: 1 1 14rem; }
/* 3. Footer pinned to the bottom of a short page */
body { display: flex; flex-direction: column; min-height: 100vh; margin: 0; }
main { flex: 1; }
/* 4. Long episode title that truncates instead of overflowing */
.row { display: flex; gap: 0.75rem; align-items: center; }
.title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }Common mistakes
- Applying flex properties to the wrong element.
justify-contenton a child does nothing; it belongs on the container, and only direct children become flex items. - Confusing the axes after `flex-direction: column`.
justify-content: centernow centres vertically, but only if the container has height to spare, which it often does not. - Using `width` on items and wondering why it is ignored. Flex-basis and shrink can override width. Use
flex: 0 0 150pxwhen you truly want a fixed size. - Text overflowing a flex row. Missing
min-width: 0, as above. - Expecting wrapped rows to align as a grid. Items on the last line stretch differently; use CSS Grid for two-dimensional alignment.
- Using `order` to rearrange content. Keyboard and screen reader order still follows the source, so visual and reading order drift apart.
How this shows up in real projects and interviews
Almost every navigation bar, toolbar, card footer and form row you meet at work is a flex container. Interviewers like practical prompts: "centre this div", "make the footer stick to the bottom", "explain the difference between justify-content and align-items", or "what does flex: 1 mean?". Answer by naming the axes first, as in this lesson, then give the property. CSS-in-JS and utility frameworks expose the same properties under shorter names.
Try it yourself
Build a player-controls row: a 48px square cover thumbnail, a title and artist block that takes all remaining space and truncates with an ellipsis, and a group of three icon buttons pinned to the right. Then make the same markup stack as a column when its container is narrower than 320px by changing flex-direction on a media query.
Show a hint
Give the middle block flex: 1 and min-width: 0. Put the buttons in their own container with display: flex and gap. A media query on the container's parent width is fine for this exercise.
Key takeaways
display: flexmakes a container's direct children flex items laid out along the main axis; the cross axis is perpendicular.justify-contentworks on the main axis andalign-itemson the cross axis, andflex-direction: columnswaps them.flex: 1means grow, shrink and start from zero basis; leftover space is shared in proportion to grow values.- Use
gap, auto margins andmin-width: 0to solve most spacing and overflow problems.
Test yourself
A trick from this lesson's ride: justify follows the flow, align crosses it; column turns the axes. Four more tricks like it wait along the route, each followed by a question.
Frequently asked questions
What is the difference between justify-content and align-items?
justify-content distributes items along the main axis, which is horizontal by default, while align-items aligns them on the cross axis, vertical by default. When you set flex-direction: column, the axes swap, so justify-content then works vertically.
What does flex: 1 mean in CSS?
It is shorthand for flex: 1 1 0%. The item may grow, may shrink, and starts from a base size of zero, so free space is shared among all items with a flex value in proportion to their grow factor. Several items with flex: 1 end up equal widths.
When should I use flexbox instead of CSS Grid?
Use flexbox for one-dimensional layouts, such as toolbars, nav bars and a row or column of items where content decides the sizes. Use grid when you need to control rows and columns together, for example page layouts or card galleries with aligned tracks. They combine well.