Flexbox.
The layout system that changed everything.
Before flexbox, centering a div was a Stack Overflow question with 3 million views. After flexbox, it's two lines. This is the most important layout system in modern CSS — every navigation bar, card layout, button group, and centered element uses it. Learn flexbox properly and 80% of "how do I arrange these" problems disappear. Every example has a live preview: click and see it work.
Add display: flex to a parent to make it a flex container. Its direct children become flex items, laid out in a row by default. Use justify-content to distribute along the main axis (row: horizontal). Use align-items to align on the cross axis (row: vertical). Use gap for spacing between items. Use flex-wrap: wrap to allow wrapping. Use flex: 1 on children to make them grow equally. Those six properties cover 95% of flexbox usage.
Before this lesson: complete Lesson 3. You need to understand sizing, spacing, and the box model before laying out multiple items.
🎯 The basics — display: flex
One line turns a parent into a flex container. Three children line up in a row.
/* The container — becomes a flex container */
.container {
display: flex;
gap: 12px;
padding: 16px;
background: rgba(21, 114, 182, 0.1);
border-radius: 8px;
}
/* The children — become flex items automatically */
.item {
background: #1572B6;
color: white;
padding: 20px;
border-radius: 6px;
text-align: center;
font-family: system-ui, sans-serif;
font-weight: 600;
}
Without display: flex — children stack vertically (default block layout):
With display: flex — children lay out in a row:
display: flex
Turns the element into a flex container. Its direct children become flex items. Only direct children — grandchildren aren't affected unless you make them flex containers too.
Main axis vs cross axis
The main axis is the direction items flow — horizontal in a row (default). The cross axis is perpendicular — vertical in a row. justify-content follows main, align-items follows cross.
Items grow by default
Flex items stretch to fill available space unless you constrain them. This is why three items in a flex container look equally wide — each got flex: 1 implicitly? No — they got equal distribution because none had a fixed basis. Set width or flex-basis to change this.
Only works on direct children
display: flex on a parent affects only its direct children. If you have a nested div inside a flex item, that nested div is NOT a flex item — it's just a normal block, unless you make it a flex container too.
The mental model: main axis and cross axis. Every flexbox property answers one question: "which axis?" justify-content works on the main axis — the direction items flow (left-right in row, top-bottom in column). align-items works on the cross axis — perpendicular to the main. If you remember "justify = main, align = cross," flexbox stops being confusing. This is the one insight that takes beginners from "I guess this works" to "I know why this works."
↔️ justify-content — main axis alignment
Six values. All about horizontal distribution (in a row layout).
/* flex-start — items packed to the start (default) */
.justify-start { display: flex; justify-content: flex-start; }
/* flex-end — items packed to the end */
.justify-end { display: flex; justify-content: flex-end; }
/* center — items packed in the middle */
.justify-center { display: flex; justify-content: center; }
/* space-between — first item at start, last at end, equal gaps between */
.justify-between { display: flex; justify-content: space-between; }
/* space-around — equal space around each item */
.justify-around { display: flex; justify-content: space-around; }
/* space-evenly — equal space between all items AND edges */
.justify-evenly { display: flex; justify-content: space-evenly; }
justify-content: flex-start (default)
justify-content: center
justify-content: space-between
justify-content: space-around
justify-content: space-evenly
flex-start / flex-end
Items packed to the start or end of the container. Default is flex-start. Use flex-end for things like "logout button on the right side of a nav."
center
Items packed in the middle. The most-used value after space-between. Use for horizontally centered buttons, logos, and nav items.
space-between
First item at start, last item at end, equal gaps between. The classic "logo on left, nav on right" layout. Probably the second-most-used value after flex-start.
space-around / space-evenly
space-around puts equal space around each item (so half the space at edges). space-evenly puts equal space between items AND at edges. The difference is subtle — visually test which you prefer.
space-between is the workhorse. Every nav bar, header, card header, and footer using flexbox starts with justify-content: space-between. It's how you put a logo on the left and links on the right with one line. .navbar { display: flex; justify-content: space-between; }. Learn this pattern — you'll use it hundreds of times.
↕️ align-items — cross axis alignment
Vertical alignment in a row layout. The property that finally made vertical centering easy.
/* stretch — items fill the cross axis (default) */
.align-stretch { display: flex; align-items: stretch; }
/* flex-start — items at the top (row layout) */
.align-start { display: flex; align-items: flex-start; }
/* flex-end — items at the bottom */
.align-end { display: flex; align-items: flex-end; }
/* center — items in the middle (VERTICAL CENTERING) */
.align-center { display: flex; align-items: center; }
/* baseline — items aligned by their text baseline */
.align-baseline { display: flex; align-items: baseline; }
align-items: stretch (default) — items fill height
align-items: flex-start — items at top
align-items: center — items centered vertically
align-items: flex-end — items at bottom
stretch (default)
Items fill the container's height. This is why nav items look equal height without any extra CSS. Set a container height and children stretch to fill it.
flex-start / flex-end
Items at top or bottom. Use flex-start for content that should anchor to the top of a card, flex-end for footer-like content at the bottom.
center
Vertical centering. Combined with justify-content: center, centers both axes. This is the modern "center a div" solution — two lines, works every time.
baseline
Items align by their text baseline. Rare but useful when items have different font sizes and you want the text to line up at the bottom of the letters instead of the box bottoms.
The perfect centering recipe. display: flex; justify-content: center; align-items: center; — three lines that center any element (or multiple elements) both horizontally and vertically. Works on any container with a defined height. This is the answer to "how do I center a div" — a question that got 3 million Stack Overflow views before flexbox existed.
📏 gap — spacing between items
One property that replaced margin hacks. Cleaner than margin-right on every child.
/* gap — consistent spacing between items, no space outside /
.gap-demo {
display: flex;
gap: 16px; / one property, applies between all items */
padding: 16px;
background: rgba(21, 114, 182, 0.1);
border-radius: 8px;
}
/* Different row and column gaps /
.gap-demo-wrap {
display: flex;
flex-wrap: wrap;
row-gap: 16px; / space between rows /
column-gap: 8px; / space between items in a row /
/ or: gap: 16px 8px; (row-gap column-gap) */
}
/* OLD WAY — margin with last-child exception */
.old-way {
display: flex;
}
.old-way > * {
margin-right: 16px;
}
.old-way > :last-child {
margin-right: 0; / required — otherwise extra space at end */
}
/* NEW WAY — one property */
.new-way {
display: flex;
gap: 16px;
}
With gap: 16px — evenly spaced items, no trailing space at the end:
With gap: 8px 24px — different row and column spacing (visible when wrapping):
gap
Applies consistent spacing between flex items. Only between them — no space before the first or after the last. One property replaces N margin declarations.
row-gap / column-gap
For wrapped flex containers, control rows and columns separately. row-gap: 16px for vertical space, column-gap: 8px for horizontal. Or gap: 16px 8px.
Browser support
Flexbox gap is supported in every modern browser since 2020. No fallback needed. Use it — it's the clean way to space flex children.
gap vs margin
gap is between items only. margin applies to every item including creating space around the outer edges. For spacing between flex children, gap is always the better choice.
Stop using margin-right on flex children. The pattern .item { margin-right: 16px; } .item:last-child { margin-right: 0; } was how everyone spaced items before gap. It required the last-child override, otherwise there was extra space at the end that broke centering and alignment. gap: 16px is one property, no exceptions, works perfectly. If you see the old margin pattern in code, replace it — the gap version is objectively better.
🔄 flex-direction — rows and columns
Switch between horizontal and vertical layouts. Also swaps the axis roles.
/* row — horizontal (default). justify = horizontal, align = vertical */
.dir-row {
display: flex;
flex-direction: row;
gap: 12px;
}
/* column — vertical. justify = vertical, align = horizontal */
.dir-column {
display: flex;
flex-direction: column;
gap: 12px;
}
/* row-reverse — horizontal, right to left */
.dir-row-reverse {
display: flex;
flex-direction: row-reverse;
gap: 12px;
}
/* column-reverse — vertical, bottom to top */
.dir-column-reverse {
display: flex;
flex-direction: column-reverse;
gap: 12px;
}
/* Card pattern — column direction with content + footer /
.card {
display: flex;
flex-direction: column;
justify-content: space-between; / space between content and footer */
height: 200px;
}
flex-direction: row (default)
flex-direction: column
flex-direction: row-reverse — order reversed
row (default)
Left to right. Main axis horizontal, cross axis vertical. justify-content = horizontal, align-items = vertical. The default for nav bars, buttons, cards.
column
Top to bottom. Main axis vertical, cross axis horizontal. justify-content = vertical, align-items = horizontal. The axis roles swap.
row-reverse / column-reverse
Same as row/column but reversed order. Rarely used — usually you'd reorder the HTML instead. Useful for right-aligned nav or reversed visual order on different screen sizes.
The axis swap
In column direction, justify-content distributes vertically (top-to-bottom) and align-items aligns horizontally. This is the single most confusing thing for beginners — remember: justify follows main, align follows cross.
The axis-swap trap. If you set flex-direction: column and expect justify-content: center to center horizontally, you'll be confused — it centers vertically. The mental model: justify-content works on the main axis (the direction of flex-direction), align-items works on the perpendicular axis. When you change direction, the roles of these properties flip. This is what makes flexbox feel unpredictable at first — but once internalized, it's clean.
📦 flex-wrap — allow wrapping
By default, flex items stay on one line. Enable wrapping for responsive layouts.
/* nowrap (default) — items squeezed into one line */
.nowrap {
display: flex;
flex-wrap: nowrap;
gap: 8px;
}
/* wrap — items wrap to next line when they run out of space */
.wrap {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
/* wrap-reverse — wraps upward instead of downward */
.wrap-reverse {
display: flex;
flex-wrap: wrap-reverse;
gap: 8px;
}
/* The card-grid pattern — the classic responsive grid /
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.card-grid > .card {
flex: 1 1 250px; / grow to fill, but never smaller than 250px before wrapping /
/ This means: try to fit as many 250px+ cards per row as possible,
then grow them equally to fill the row */
}
/* The tag-cloud pattern — small chips that wrap */
.tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.tag {
padding: 6px 12px;
background: rgba(21, 114, 182, 0.2);
border-radius: 999px;
font-size: 13px;
}
flex-wrap: nowrap (default) — everything on one line, squished
flex-wrap: wrap — items wrap to next line
Tag cloud — the classic use case
nowrap (default)
All items on one line. If they don't fit, they shrink (or overflow if flex-shrink: 0). Use for navs and toolbars that should never wrap.
wrap
Items wrap to the next line when they run out of space. Essential for responsive layouts, card grids, and tag clouds. Usually what you want on mobile.
wrap + align-content
When you have multiple rows, align-content controls vertical spacing between rows. align-content: flex-start | center | space-between | .... Without wrapping, align-content has no effect.
flex-basis for wrapping
flex-basis: 250px on children means "minimum 250px before wrapping." Combined with flex: 1 1 250px, cards try to fit at 250px+ and grow to fill the row. The classic responsive-grid pattern.
The responsive card grid — without media queries. display: flex; flex-wrap: wrap; gap: 16px; on the container plus flex: 1 1 250px; on each card gives you a grid that auto-fits cards based on viewport width. One row on mobile (250px fits), two on tablet, three on desktop. No media queries needed. This pattern is used by Tailwind, Bootstrap, and every modern site. It's the flexbox equivalent of "grid."
⚖️ flex-grow / shrink / basis — sizing children
How children respond when there's too much or too little space.
/* flex: grow shrink basis — the shorthand */
/* Equal columns — all children grow equally /
.equal-children > * {
flex: 1; / shorthand for flex: 1 1 0% */
}
/* Different proportions — 2:1 ratio */
.two-to-one > *:nth-child(1) { flex: 2; }
.two-to-one > *:nth-child(2) { flex: 1; }
/* Fixed width — doesn't grow or shrink /
.fixed {
flex: 0 0 200px; / don't grow, don't shrink, exactly 200px */
}
/* Grow to fill but never shrink below content size */
.grow-only {
flex: 1 0 auto;
}
/* Responsive grid — grow, shrink, minimum 250px before wrapping */
.responsive {
flex: 1 1 250px;
}
/* The most common: sidebar with fixed width + main content /
.layout {
display: flex;
gap: 16px;
}
.layout .sidebar {
flex: 0 0 250px; / fixed 250px sidebar /
}
.layout .main {
flex: 1; / take all remaining space */
}
flex: 1 on all children — equal widths
flex: 2 on first, flex: 1 on others — 2:1 ratio
Sidebar (fixed) + Main (fills space)
fixed 150px
flex: 1
flex: 1
The most-used flex value. Means "grow to fill available space, shrink if needed, start from zero." All children with flex: 1 share space equally. Use for equal columns.
flex: 2 vs flex: 1
Proportional widths. flex: 2 gets twice the space of flex: 1. Use for asymmetrical layouts — a 2:1 column split, a sidebar that's narrower than main.
flex: 0 0 200px
Don't grow, don't shrink, exactly 200px. Use for sidebars, icons, avatars — things with a fixed size. If you want a fixed-width item, don't use flex: 1 on it.
flex: 1 1 250px
The responsive-grid pattern. "Grow, shrink, but start at 250px minimum before wrapping." Combined with flex-wrap: wrap, this creates grids that adapt to viewport width automatically.
The classic mistake: flex: 1 on everything. If you put flex: 1 on every child, they all become equal — even if one has an image and one has a short text. This is often what you want for navs and cards, but not for sidebars (which should be fixed width). Use flex: 1 where you want equal widths, and flex: 0 0 auto or flex: 0 0 250px where you don't.
🎯 Real-world patterns
Five layouts that cover most of what you'll build. Each is a preview.
/* 1. NAV BAR — logo left, links right */
.nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 24px;
background: #1e293b;
}
/* 2. CARD with content + footer */
.card {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 200px;
padding: 20px;
background: #1e293b;
}
/* 3. HORIZONTALLY CENTERED HERO */
.hero {
display: flex;
justify-content: center;
align-items: center;
min-height: 300px;
text-align: center;
}
/* 4. CARD GRID — auto-fit, wraps responsively */
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.card-grid > * {
flex: 1 1 250px;
}
/* 5. SIDEBAR LAYOUT — fixed sidebar + flexible main */
.layout {
display: flex;
gap: 24px;
}
.layout .sidebar { flex: 0 0 200px; }
.layout .main { flex: 1; }
1. Nav bar — space-between
2. Card — column + space-between
Card content sits at the top.
3. Hero — centered both axes
Centered content
Both axes centered
4. Card grid — flex-wrap auto-fit
5. Sidebar layout
200px fixed
flex: 1
These five patterns cover 80% of layouts. Every nav bar, every card, every centered hero, every grid of cards, every sidebar layout — all use one of these five. Learn them and you can build almost anything without referencing docs. They're not just examples — they're the standard patterns every professional uses. Copy them into your next project and adapt.
🏆 Challenge
Three tasks. Each exercises a different flexbox pattern.
Task 1 — Perfect centering
Build a container that's 400px tall with a centered card inside. The card should be:
1. Max 300px wide
2. White background (or dark surface)
3. 24px padding
4. Border radius 12px
5. Contains a title and a paragraph
6. Centered both horizontally and vertically in the container
Use only display: flex; justify-content: center; align-items: center; on the container. Test by resizing the browser — the card should stay centered.
Task 2 — Responsive card grid
Build a grid of 6 cards that:
1. Shows 1 card per row on mobile (<600px)
2. Shows 2 cards per row on tablet (600–1000px)
3. Shows 3 cards per row on desktop (>1000px)
4. Has consistent spacing between cards
5. Uses flexbox (not grid)
Hint: display: flex; flex-wrap: wrap; gap: 16px; on the container + flex: 1 1 300px; on the cards. No media queries needed — the cards wrap automatically based on available space.
Task 3 — Full navigation bar
Build a complete nav bar with:
1. Logo on the left
2. Three links in the center
3. A "Sign in" button on the right
4. All items vertically centered
5. Consistent spacing between items
6. On mobile (under 600px), stack everything vertically
Use flexbox with justify-content: space-between and align-items: center. For the mobile stacking, use @media (max-width: 600px) { .nav { flex-direction: column; } }. This is the exact pattern every real nav bar uses.
⚠️ Common mistakes
The flexbox bugs that catch every beginner.
justify-content controls the MAIN axis (direction of flex-direction). align-items controls the CROSS axis. If justify-content: center isn't centering vertically, you're in row direction — use align-items: center for that.
Without flex-wrap: wrap, cards squeeze into one row and become unreadable on mobile. Fix: add flex-wrap: wrap to any container with many items.
The old pattern requires .item:last-child { margin-right: 0 } — otherwise there's extra space at the end. Fix: use gap: 16px. One property, no exceptions.
If all children get flex: 1, they're all equal width — even a 50px avatar and 500px text become equal. Fix: only use flex: 1 on items that should share space. Use flex: 0 0 auto for fixed-size items.
Items default to stretch — they fill the container's height. If your logo and nav links look off-center vertically, add align-items: center to the nav container.
Flex items grow by default — a card with less content still fills its row. If you want content-sized items (like tags or buttons), use flex: 0 0 auto or flex: 0 1 auto.
width: 200px on a flex child can be overridden by flex-grow/shrink. Fix: use flex: 0 0 200px (fixed), or flex: 1 1 200px (grows to 200px+, then fills).
display: flex on a parent affects only direct children. If you need grandchildren arranged in a row, make the child a flex container too. Nested flex is normal and expected.
➡️ What's next
Flexbox handles rows and columns. Next: full two-dimensional layouts.
❓ Frequently asked questions
What is CSS flexbox?
Flexbox is a CSS layout system for arranging items in a single dimension — either a row or a column. It replaced older techniques like float and inline-block because it handles alignment, spacing, and sizing automatically. One line, display: flex, enables a whole toolkit of properties. It's the most-used layout tool in modern CSS.
What is the difference between justify-content and align-items?
justify-content controls alignment along the MAIN axis (horizontal in row direction). align-items controls alignment along the CROSS axis (perpendicular). In a default row layout, justify-content = horizontal, align-items = vertical. If you change flex-direction to column, they swap roles.
What does flex-direction do?
flex-direction sets the main axis — the direction items are laid out. row (default): left to right. column: top to bottom. Changing direction swaps the roles of justify-content and align-items. row is the default and what you'll use 90% of the time.
What is the difference between gap and margin in flexbox?
gap applies consistent spacing between flex items — only between them, never outside. margin applies to each child including around outer edges. gap is cleaner: one property replaces margin-right on every child plus the last-child exception. Use gap for spacing between flex children.
How do I center an element with flexbox?
Two properties on the parent: display: flex; justify-content: center; align-items: center. justify centers horizontally, align centers vertically. Works regardless of child size. If the parent has a fixed height, the child centers vertically within it.
What is flex-wrap and why does it matter?
By default, flex items try to fit in one row. flex-wrap: wrap lets items wrap to the next line when they run out of space. Essential for responsive layouts, tags, and card grids. Add gap for spacing that applies both horizontally and vertically.
What is the difference between flex-grow, flex-shrink, and flex-basis?
flex-grow: how much a child grows to fill extra space (default 0). flex-shrink: how much a child shrinks when space is tight (default 1). flex-basis: initial size before growing/shrinking (default auto). The shorthand flex: 1 means flex-grow: 1, flex-shrink: 1, flex-basis: 0.
Should I use flexbox or grid?
Flexbox is one-dimensional (row OR column), grid is two-dimensional (rows AND columns). Use flexbox for component layouts: nav bars, button groups, card contents. Use grid for page layouts: dashboards, image galleries, article layouts. Modern sites often use both — grid for structure, flexbox for components.