CSS Flexbox: A Practical Guide
Learn how Flexbox axes, sizing, wrapping, and alignment work, then build and debug practical layouts with runnable examples.
CSS Flexbox arranges items along one axis at a time. Set display: flex on a parent to create a flex container; its in-flow children become flex items. The flex-direction property sets the main axis, and the cross axis runs perpendicular to it. Use Flexbox for relationships in one direction, such as a navigation bar, toolbar, or row of cards. Use CSS Grid when you need to control rows and columns together.
This guide builds a responsive card row and a compact toolbar, explains sizing and alignment, and shows how to diagnose common layout surprises. It assumes basic HTML and CSS.
1. The Flexbox mental model
Think of the container as laying its items out along a main axis. The cross axis is perpendicular. The default flex-direction is row, so the main axis follows the inline direction (left to right in a typical left-to-right page) and the cross axis follows the block direction. With column, those roles switch. Axis-relative language stays useful when direction or writing mode changes.
.toolbar {
display: flex;
flex-direction: row;
}
.stack {
display: flex;
flex-direction: column;
}
The element with display: flex is the flex container; its in-flow children are flex items. A flex item can itself be another flex container, so layouts can be composed from nested one-dimensional groups.
| Property | Axis or behavior | Typical use |
|---|---|---|
flex-direction |
Sets main axis | row, row-reverse, column, column-reverse |
justify-content |
Distributes items along the main axis | Space a toolbar or center a short row |
align-items |
Aligns items along the cross axis | Vertically center controls in a row |
align-self |
Overrides cross-axis alignment for one item | Align one card differently |
flex-wrap |
Controls whether items stay on one line | Let cards move to additional lines |
2. Build a responsive card group
Start with a small, runnable HTML file. Save it as flexbox.html and open it in a browser. Resize the viewport to observe wrapping.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Flexbox cards</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; padding: 2rem; font: 1rem/1.5 system-ui, sans-serif; color: #172033; }
.cards {
display: flex;
flex-flow: row wrap;
gap: 1rem;
align-items: stretch;
}
.card {
flex: 1 1 14rem;
min-width: 0;
padding: 1.25rem;
border: 1px solid #cbd5e1;
border-radius: .75rem;
background: #f8fafc;
}
.card h2 { margin: 0 0 .5rem; font-size: 1.15rem; }
.card p { margin: 0; }
</style>
</head>
<body>
<main class="cards">
<article class="card"><h2>Plan</h2><p>Choose a sensible starting point.</p></article>
<article class="card"><h2>Build</h2><p>Arrange content around its relationships.</p></article>
<article class="card"><h2>Review</h2><p>Resize and inspect the result.</p></article>
</main>
</body>
</html>
flex-flow is shorthand for flex-direction and flex-wrap. Here it means items flow in a row and wrap onto additional flex lines when they no longer fit. gap adds consistent space between items without adding margins to the outer edges. Each line is laid out independently along the main axis; wrapping does not make Flexbox a two-dimensional grid.
Make a toolbar
For a row with a title at the start and actions at the end, let the title consume available positive space:
.toolbar {
display: flex;
align-items: center;
gap: .75rem;
}
.toolbar__title { margin-right: auto; }
.toolbar__actions { display: flex; gap: .5rem; }
The nested actions group is another flex container. The auto margin uses available space to separate the title from the actions. If the toolbar must remain usable on narrow screens, allow it to wrap or change its direction at a suitable breakpoint.
3. Align and distribute items
justify-content works along the main axis; align-items works along the cross axis. Their effect depends on flex-direction, not on fixed assumptions about horizontal and vertical directions.
.centered {
display: flex;
justify-content: center;
align-items: center;
min-height: 12rem;
}
.spread {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
}
The first example centers children along both axes inside a box with a definite minimum height. Without available space on an axis, there may be no visible movement to distribute. align-self sets the cross-axis alignment for an individual item and overrides the container’s align-items for that item.
Common justify-content values include flex-start, flex-end, center, space-between, space-around, and space-evenly. Common align-items values include stretch, flex-start, flex-end, center, and baseline. For most interfaces, prefer gap for fixed spacing and alignment properties for distributing leftover space or aligning edges.
4. Understand flexible sizing
Flex item sizing uses three related values:
flex-basisis the starting size along the main axis before free space is distributed. It can be a length, percentage, orauto.flex-growcontrols how an item shares positive free space. A larger factor gets a larger share relative to the other grow factors.flex-shrinkcontrols how an item participates in reducing sizes when the line has insufficient space. Shrinking is weighted by the factors and base sizes.
The shorthand is flex: <grow> <shrink> <basis>. The card example’s flex: 1 1 14rem gives each card a 14rem starting basis, permits growth into spare space, and permits shrinking as the line gets tight. With wrapping enabled, the browser first forms lines; sizing is then resolved within each line.
/* Grow from a content or declared basis; shrink if needed. */
.item { flex: 1 1 12rem; }
/* Do not grow, but allow shrinking from a 16rem basis. */
.sidebar { flex: 0 1 16rem; }
/* Keep the item's basis size: neither grow nor shrink. */
.badge { flex: 0 0 5rem; }
| Keyword | Equivalent components | Meaning |
|---|---|---|
initial |
0 1 auto |
Do not grow; allow shrinking from the automatic basis |
auto |
1 1 auto |
Allow growth and shrinking from the automatic basis |
none |
0 0 auto |
Neither grow nor shrink |
1 |
Grow factor 1; basis effectively zero in the shorthand’s one-number form | Share available main-axis space equally with other equally weighted items |
The shorthand has defined defaults that can be easy to misread. Spell out all three values when exact behavior matters. Min-content constraints can also prevent an item from shrinking as far as expected; for a long string or wide child, try min-width: 0 on a row item (or the corresponding minimum size on the main axis) and decide how overflow should appear.
5. Flexbox or Grid?
Choose based on the relationship the design needs. MDN describes Flexbox as one-dimensional and Grid as two-dimensional; the W3C specification describes Flexbox as a box model optimized for user interface design. [MDN CSS layout; W3C Flexbox specification]
| Need | Good starting choice | Reason |
|---|---|---|
| One row of controls or a vertical stack | Flexbox | Items relate along one primary direction |
| Cards that should flow and wrap naturally | Flexbox | Line wrapping is part of the desired behavior |
| Aligned rows and columns with coordinated tracks | Grid | Both dimensions matter to placement and sizing |
| Explicit placement across a two-dimensional area | Grid | Tracks and positions need joint control |
These are starting points, not rigid rules. A page can use Grid for its main structure and Flexbox inside a toolbar, or the reverse. MDN’s layout cookbook has pattern-based examples.
6. Debug Flexbox in browser DevTools
- Select a suspected flex item in the Elements or Inspector panel, then move to its parent to confirm which element has
display: flex. - Check computed
display,flex-direction,flex-wrap,gap, item basis, grow and shrink values, and minimum sizes. A crossed-out declaration may have lost the cascade. - Turn on the browser’s flex layout overlay, when available, to see container boundaries, axes and item sizing. Names and controls vary by browser.
- Toggle one property at a time: change direction, disable wrapping, add a temporary outline, or adjust basis. Observe which axis changes.
- Check the parent’s available size and all ancestor constraints. Flexbox cannot distribute space that the container does not have.
MDN’s CSS debugging guide covers browser DevTools and compatibility information. The W3C CSS Flexible Box Layout Module Level 1 page found for this guide is a Candidate Recommendation Draft dated 14 October 2025; W3C cautions that the draft can be updated, replaced, or obsoleted, so it is not a final Recommendation. [W3C status and specification]
7. Common problems and fixes
| Symptom | Likely cause | Fix to try |
|---|---|---|
| Items remain on one line and overflow | Default flex-wrap is nowrap |
Set flex-wrap: wrap, or use flex-flow: row wrap |
justify-content appears to do nothing |
No free space exists on the main axis, or the wrong axis is assumed | Check container width/height, direction, and item sizes |
| Centering fails vertically or horizontally | Alignment property was chosen by screen direction, or the container has no spare cross-axis size | Identify main and cross axes; give the container enough size and use the matching alignment property |
| Long text forces a card wider than expected | Automatic minimum size or unbreakable content prevents shrinking | Try min-width: 0 on a row item; add overflow handling or wrapping for its content |
| Cards on the last wrapped row are wider | Free space is distributed separately on each flex line | Use Grid if all rows need coordinated columns; otherwise set a suitable basis or maximum width |
| Items appear in an unexpected order | row-reverse, column-reverse, or an order value changes visual order |
Prefer source order that is meaningful; inspect both visual and keyboard reading order |
| Items refuse to shrink | Minimum size, intrinsic content, fixed width, or child sizing constrains them | Inspect min/max sizes and descendants; reduce the basis or minimum only if the content remains usable |
| Spacing seems larger than expected | gap, margins, padding, or distributed free space are all contributing |
Temporarily disable each spacing source in DevTools, then keep the intended one |
| CSS declaration has no effect | Another rule overrides it, selector misses, or declaration is invalid | Inspect matched and computed styles, cascade order, and syntax |
8. Performance, reliability, and cost
Flexbox is a native CSS layout method and requires no library or service. For ordinary interface layouts, focus on clear container boundaries, avoiding needless overrides, and checking behavior at narrow and wide sizes. If a page visibly jumps while loading, inspect changing content, font metrics, image dimensions, and scripts that alter the layout; Flexbox alone does not guarantee a stable page.
When documenting a layout issue, a screenshot can help show the viewport and resulting arrangement, but it cannot replace inspecting the DOM, computed styles, or responsive behavior. You can capture a page manually in a browser, or use ScreenshotNeo’s website screenshot API when you need repeatable page captures. Its API request can return PNG, JPEG, WebP, or PDF; the call below requests a screenshot of a page. See the ScreenshotNeo documentation for options and request details.
Or skip the browser setup
Use one GET request to capture a page. Store your API key securely and replace the example target URL if needed.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo accepts cookie or consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for 1,000 free screenshots a month, with no card required.
9. FAQ
Can a flex item also be a flex container?
Yes. Apply display: flex to that item to lay out its own children. The outer container controls the item as a whole; the nested container controls its children.
Does gap work when Flexbox wraps?
Yes. It creates spacing between items and between flex lines. The resulting layout still sizes each line independently.
Should I use order to rearrange content?
Use it carefully. Visual order can differ from source and keyboard order, which can make an interface confusing. Prefer meaningful document order and use visual reordering only when it preserves a sensible reading and interaction sequence.
Where can I practice?
Start with MDN’s Flexbox learning guide. It also identifies an interactive Scrimba guide as a learning-partner resource.


