CSS flexbox layout
CSS Flexbox is a layout model that arranges items along a single axis (row or column), automatically handling spacing, alignment, and sizing inside a container.
Theory
TL;DR
- Flexbox is like a conveyor belt: items space out, align, and resize to fill the container without fixed positions
- Main difference from floats: flexbox gives predictable alignment along two axes, main and cross
- Use for 1D layouts (navbars, button groups); switch to CSS Grid for 2D (dashboards)
- Default
flex-directionisrow(left to right) justify-contentcontrols the main axis;align-itemscontrols the cross axis
Quick example
.container {
display: flex; /* Turns children into flex items */
justify-content: space-between; /* Spreads items along main axis */
align-items: center; /* Centers on cross axis */
height: 100px;
}Three items inside .container spread horizontally with equal gaps, all vertically centered. No floats, no margin math.
Compared to float-based layouts
Before flexbox, vertical centering was famously painful. Float-based layouts wrapped unpredictably, and inline-block required white-space hacks. In production, I've seen more float layouts break from wrapping bugs than from almost anything else in CSS.
Flexbox replaces all of that with two properties. The container becomes a flexible unit. Items grow or shrink to fill available space along the main axis. You don't calculate margins for equal spacing; you tell the container how to distribute the free space.
The two axes
Every flex container has two axes:
- Main axis - defined by
flex-direction. Default isrow(left to right). - Cross axis - perpendicular to the main axis.
When flex-direction: column, the main axis becomes vertical. This flips which property controls what: justify-content now aligns vertically, and align-items aligns horizontally. This trips up a lot of developers.
Container properties
| Property | What it does |
|---|---|
flex-direction | Sets main axis: row, row-reverse, column, column-reverse |
flex-wrap | Whether items wrap: nowrap (default), wrap, wrap-reverse |
justify-content | Spacing on main axis: flex-start, center, space-between, space-around, space-evenly |
align-items | Alignment on cross axis: stretch, center, flex-start, flex-end, baseline |
align-content | Distributes multiple wrapped lines (only with flex-wrap: wrap) |
gap | Space between items |
Item properties
| Property | What it does |
|---|---|
flex-grow | How much an item grows relative to siblings (default: 0) |
flex-shrink | How much it shrinks (default: 1) |
flex-basis | Initial size before growing or shrinking (default: auto) |
flex | Shorthand: flex-grow flex-shrink flex-basis |
align-self | Overrides align-items for one item |
order | Changes visual order without touching HTML |
The shorthand flex: 1 expands to flex: 1 1 0%. That means "grow, shrink, start from zero width." This is how you get equal-width columns.
When to use
- Simple row or column (navbar, button group) - flexbox
- Content that needs to wrap (card grid, tag list) - flexbox with
flex-wrap: wrap - Two-dimensional layout (dashboard, page grid) - CSS Grid instead
- Replacing old float alignment bugs - flexbox cleans them up
How the browser handles this
When the browser parses display: flex, it creates a flex formatting context. It calculates free space along the main axis using flex factors from the flex shorthand, runs each item through a hypothetical main size calculation, then distributes leftover space via justify-content. Chrome's Blink engine resolves this in a single reflow pass, which is why flexbox is faster than float-based layouts for alignment work.
Common mistakes
Forgetting display: flex on the parent:
.container { /* no display: flex */ }
.item { flex: 1; } /* Ignored - items still stack vertically */flex: 1 on a child does nothing without the parent being a flex container. Add display: flex to .container.
Using align-items on the wrong axis in column direction:
.container {
display: flex;
flex-direction: column;
align-items: center; /* Centers horizontally, NOT vertically */
}In column direction, align-items controls horizontal alignment. To center items vertically in a column, use justify-content: center.
Not setting flex-wrap when items overflow:
.container { display: flex; flex-wrap: nowrap; width: 200px; }
.item { width: 100px; } /* 5 items cause overflow */nowrap is the default. With many items, they squeeze or overflow. Add flex-wrap: wrap to let them move to the next line.
Assuming equal widths without flex: 1:
.container { display: flex; }
.item { /* default: flex: 0 1 auto - sizes by content */ }Without flex: 1, items don't split space equally. They size based on their content. Use flex: 1 on items you want to grow equally.
min-width interaction with flex-grow:
.container { display: flex; width: 300px; }
.item1 { flex: 1; min-width: 100px; }
.item2 { flex: 2; }item1 enforces its 100px minimum first, then item2 takes the rest (200px). Without min-width, item1 can shrink below what you expect.
Real-world usage
- React navbars and headers (Material-UI App Bar uses flex internally)
- Bootstrap
.d-flexutility classes - Tailwind
flex,justify-between,items-centerfor headers and cards - VS Code sidebar - flex for resizable panels
Follow-up questions
Q: What is the default flex-direction and how does row-reverse change it?
A: Default is row (left to right). row-reverse flips to right to left, reversing the main axis start and end. The DOM order stays the same; only the visual order changes.
Q: What is the difference between justify-content: space-between and space-around?
A: space-between puts equal space between items, nothing at the edges. space-around gives each item half the gap on each side, so edge items have half as much space from the container wall as items have between each other.
Q: How does align-content differ from align-items?
A: align-items aligns items within a single line on the cross axis. align-content distributes multiple wrapped lines. It only has an effect when flex-wrap: wrap creates more than one row.
Q: Given flex: 0 0 200px with margin: auto, what happens on a narrow container?
A: The item stays at 200px and will not shrink (no flex-shrink). margin: auto absorbs free space on the main axis, centering the item when space is available. If the container is narrower than 200px, the item overflows unless flex-wrap is set.
Examples
Navbar layout
A typical navigation bar: logo on the left, links on the right.
<nav class="navbar">
<h1>Logo</h1>
<ul>
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</nav>.navbar {
display: flex;
justify-content: space-between; /* Logo left, nav right */
align-items: center; /* Both vertically centered */
padding: 0 20px;
background: #333;
}
.navbar ul {
display: flex; /* Nested flex: menu items in a row */
list-style: none;
gap: 1rem;
}Nested flex containers work fine. The ul becomes its own flex container for the li items inside it.
Sidebar and main content
A page layout with a fixed sidebar and fluid main area.
.layout {
display: flex;
min-height: 100vh;
}
.sidebar { flex: 0 0 250px; } /* Fixed 250px, no grow, no shrink */
.main { flex: 1; } /* Takes all remaining width */
.aside { flex: 0 0 200px; } /* Fixed 200px on the right */0 0 250px means "start at 250px, stay there." flex: 1 means "take what is left." That combination is one of the most common flex patterns in production.
Sticky footer
Footer always at the bottom, even on short pages.
body {
display: flex;
flex-direction: column; /* Header, main, footer stack vertically */
min-height: 100vh;
}
main {
flex: 1; /* Grows to fill remaining space, pushing footer down */
}flex: 1 on main is the key. It grows to absorb all available vertical space, so the footer stays at the bottom regardless of how much content the page has.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.