CSS Flexbox Complete Guide: Layouts Explained From Scratch
Before Flexbox arrived, building even a simple two-column layout in CSS felt like solving a puzzle with missing pieces. Developers leaned on floats, negative margins, and inline-block hacks just to centre a button on screen — and those solutions broke the moment the screen size changed. Flexbox (short for Flexible Box Layout) was introduced specifically to fix this pain point, and it's now the backbone of nearly every modern web layout you see.
The Two Players: Flex Container and Flex Children
Flexbox always involves a relationship between two things: a parent element called the flex container and the direct children inside it called flex items. Think of it like a food tray (the container) holding individual dishes (the items). When you apply display: flex to the tray, it immediately gains superpowers — it can decide how to line up, space out, and resize every dish automatically.
The key rule beginners miss: Flexbox properties split into two groups. Some properties go on the container (like justify-content and align-items) and some go on the items (like flex-grow and align-self). Mixing them up on the wrong element is the number-one source of confusion.
To activate Flexbox you write exactly one CSS rule on the parent: display: flex. That single line transforms the layout behaviour of every direct child underneath it. Children don't need to do anything — they become flex items the moment their parent becomes a flex container.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Flexbox — Container vs Items</title> <style> /* ── THE FLEX CONTAINER ──────────────────────────────── */ .recipe-card-row { display: flex; /* This single line activates Flexbox */ background: #f0f4f8; padding: 16px; gap: 12px; /* Adds equal breathing space between items */ } /* ── THE FLEX ITEMS (children of .recipe-card-row) ───── */ .recipe-card { background: #ffffff; border: 1px solid #d1d9e0; border-radius: 8px; padding: 20px; /* No width needed — Flexbox handles distribution */ } </style> </head> <body> <!-- The PARENT is the flex container --> <div class="recipe-card-row"> <!-- Each direct child automatically becomes a flex item --> <div class="recipe-card">🍕 Pizza</div> <div class="recipe-card">🍣 Sushi</div> <div class="recipe-card">🥗 Salad</div> </div> </body> </html>
Understanding the Two Axes — Main and Cross
Here's the concept that unlocks everything else in Flexbox: there are always two invisible lines running through your flex container. The main axis is the direction your items flow. The cross axis is perpendicular to it — at a right angle.
By default the main axis runs left to right (horizontal), so items line up in a row. You change this with flex-direction. Set it to column and the main axis flips to top-to-bottom, stacking items vertically like a list.
Why does this matter? Because the alignment properties — justify-content and align-items — are defined relative to these axes, not to specific directions. justify-content always controls the main axis. align-items always controls the cross axis. Once this clicks, you'll never forget which property does what.
Think of a road (main axis) and a pavement beside it (cross axis). justify-content moves cars along the road. align-items moves them sideways onto or off the pavement.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Flexbox Axes — Main vs Cross</title> <style> .navigation-bar { display: flex; flex-direction: row; /* Default: main axis = LEFT → RIGHT */ justify-content: space-between; /* Spread items along the MAIN axis */ align-items: center; /* Centre items on the CROSS axis (top↕bottom) */ background: #1a202c; padding: 0 24px; height: 60px; } .nav-logo { color: #63b3ed; font-weight: bold; font-size: 1.2rem; } .nav-links { display: flex; /* Nested flex container for the link group */ gap: 20px; list-style: none; margin: 0; padding: 0; } .nav-links a { color: #e2e8f0; text-decoration: none; } /* ── COLUMN DIRECTION EXAMPLE ─────────────────────────── */ .sidebar-menu { display: flex; flex-direction: column; /* Main axis flips: now TOP → BOTTOM */ align-items: flex-start; /* Items hug the LEFT side of the cross axis */ gap: 8px; background: #2d3748; padding: 16px; width: 200px; } .sidebar-menu a { color: #e2e8f0; text-decoration: none; padding: 8px 12px; border-radius: 4px; width: 100%; } .sidebar-menu a:hover { background: #4a5568; } </style> </head> <body> <!-- HORIZONTAL nav (flex-direction: row) --> <nav class="navigation-bar"> <span class="nav-logo">TheCodeForge</span> <ul class="nav-links"> <li><a href="#">Home</a></li> <li><a href="#">Articles</a></li> <li><a href="#">About</a></li> </ul> </nav> <!-- VERTICAL sidebar (flex-direction: column) --> <div class="sidebar-menu"> <a href="#">Dashboard</a> <a href="#">Projects</a> <a href="#">Settings</a> </div> </body> </html>
Alignment Deep Dive — justify-content, align-items, and align-self
Now that you know the axes exist, let's master the properties that control them. These three are responsible for roughly 80% of every layout you'll ever build with Flexbox.
justify-content accepts values like flex-start (pack left), flex-end (pack right), center (middle), space-between (first item at start, last at end, even gaps between), and space-around (equal space on both sides of each item). space-between is the most commonly used in real nav bars and card grids.
align-items works on the cross axis: stretch (default — items fill the container height), center, flex-start, and flex-end.
align-self is the escape hatch. It goes on an individual flex item and overrides align-items just for that one child. It's perfect when one card in a row needs to sit at the top while the rest are centred.
The classic 'centre a div on screen' problem — which tortured CSS developers for years — is solved in two lines with Flexbox.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Flexbox Alignment Examples</title> <style> * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: sans-serif; padding: 24px; background: #f7fafc; } /* ── EXAMPLE 1: Classic centred hero section ─────────── */ .hero-section { display: flex; justify-content: center; /* Centre content along main axis (horizontal) */ align-items: center; /* Centre content along cross axis (vertical) */ height: 200px; background: #2b6cb0; border-radius: 8px; margin-bottom: 24px; } .hero-section h1 { color: white; font-size: 2rem; } /* ── EXAMPLE 2: space-between for a dashboard stat row ─ */ .stats-row { display: flex; justify-content: space-between; /* Even gaps between stat cards */ align-items: stretch; /* All cards match the tallest card's height */ gap: 16px; margin-bottom: 24px; } .stat-card { background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 20px; flex: 1; /* Each card takes an equal share of available width */ } .stat-card.featured { align-self: flex-start; /* THIS card won't stretch — it stays its natural height */ background: #ebf8ff; border-color: #63b3ed; } .stat-card h2 { font-size: 2rem; color: #2d3748; } .stat-card p { color: #718096; font-size: 0.85rem; } </style> </head> <body> <!-- EXAMPLE 1: Perfect centring in two CSS rules --> <div class="hero-section"> <h1>Welcome to TheCodeForge</h1> </div> <!-- EXAMPLE 2: Dashboard stats with align-self override --> <div class="stats-row"> <div class="stat-card"> <h2>1,284</h2> <p>Articles Published</p> <p>This is a longer description that makes this card taller than others.</p> </div> <!-- This card uses align-self to opt OUT of stretching --> <div class="stat-card featured"> <h2>98%</h2> <p>Reader Satisfaction</p> </div> <div class="stat-card"> <h2>42k</h2> <p>Monthly Readers</p> </div> </div> </body> </html>
flex-grow, flex-shrink, and flex-basis — Making Items Flexible
This is where 'Flexible' in Flexbox actually earns its name. These three properties control how each individual item behaves when there's extra space or not enough space.
flex-basis sets the starting size of an item before any space is distributed — think of it as the item's 'wish' for how big it wants to be. flex-grow says 'if there's leftover space, I want this share of it'. A value of 1 means 'take a fair share'. A value of 2 means 'take twice as much as items with 1'. flex-shrink works in reverse — when space runs out, how much should this item shrink? The default is 1, meaning all items shrink equally.
The shorthand flex: 1 is the most common thing you'll write in real projects. It expands to flex-grow: 1, flex-shrink: 1, flex-basis: 0%, meaning 'share all available space equally among siblings'.
flex-wrap is also critical here. By default, Flexbox squeezes all items into one line. Set flex-wrap: wrap and items spill onto the next row when they run out of room — essential for responsive card grids.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>flex-grow, flex-shrink, flex-wrap</title> <style> * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: sans-serif; padding: 24px; background: #f7fafc; } /* ── EXAMPLE 1: Unequal columns (sidebar + main content) ── */ .page-layout { display: flex; gap: 16px; margin-bottom: 32px; height: 120px; } .page-sidebar { flex: 0 0 220px; /* flex-grow:0, flex-shrink:0, flex-basis:220px */ /* NEVER grow, NEVER shrink — always exactly 220px */ background: #2d3748; color: white; padding: 16px; border-radius: 8px; } .page-main-content { flex: 1; /* Shorthand: grow to fill ALL remaining space */ background: white; border: 1px solid #e2e8f0; padding: 16px; border-radius: 8px; } /* ── EXAMPLE 2: Responsive card grid with flex-wrap ─────── */ .article-grid { display: flex; flex-wrap: wrap; /* Items wrap to the next row when space runs out */ gap: 16px; } .article-card { flex: 1 1 280px; /* Grow and shrink, but never go below 280px wide */ /* This creates a naturally responsive grid */ background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 20px; } .article-card h3 { color: #2d3748; margin-bottom: 8px; } .article-card p { color: #718096; font-size: 0.9rem; } </style> </head> <body> <!-- LAYOUT: Fixed sidebar + Fluid main area --> <div class="page-layout"> <aside class="page-sidebar">Sidebar (always 220px)</aside> <main class="page-main-content">Main content (takes all remaining space)</main> </div> <!-- GRID: Cards wrap to new rows on small screens --> <div class="article-grid"> <div class="article-card"> <h3>Getting Started with CSS</h3> <p>Learn the fundamentals of styling web pages from zero.</p> </div> <div class="article-card"> <h3>JavaScript Promises Explained</h3> <p>Understand async code without losing your mind.</p> </div> <div class="article-card"> <h3>React Hooks Deep Dive</h3> <p>Master useState, useEffect, and custom hooks.</p> </div> <div class="article-card"> <h3>Node.js for Beginners</h3> <p>Build your first server-side app step by step.</p> </div> </div> </body> </html>
| Feature / Aspect | CSS Flexbox | CSS Grid |
|---|---|---|
| Best used for | Single-axis layouts (row OR column) | Two-axis layouts (rows AND columns simultaneously) |
| Direction control | flex-direction: row | column | Rows and columns defined together with grid-template |
| Item alignment | justify-content + align-items | justify-items + align-items (same concept, grid context) |
| Content-driven sizing | Yes — items size to their content naturally | Possible but layout is more structure-driven |
| Responsive without media queries | Yes — flex-wrap + flex-basis handles most cases | Yes — with repeat(auto-fill, minmax()) pattern |
| Typical use cases | Navbars, button groups, card rows, centring | Page layouts, photo galleries, dashboard grids |
| Browser support | All modern browsers + IE11 (with prefixes) | All modern browsers, IE11 partial support only |
| Learning curve | Lower — fewer properties to learn first | Higher — requires understanding both axes at once from the start |
🎯 Key Takeaways
- display: flex goes on the PARENT container — that one rule makes all direct children into flex items automatically.
- justify-content controls the MAIN axis, align-items controls the CROSS axis — and those axes SWAP when flex-direction changes to column.
- flex: 1 is the most powerful shorthand you'll write — it means 'grow and shrink equally with siblings', producing fluid layouts without hardcoded widths.
- flex-wrap: wrap combined with flex: 1 1 [min-width] on children creates a fully responsive card grid with zero media queries — this pattern replaces entire grid frameworks.
⚠ Common Mistakes to Avoid
- ✕Mistake 1: Putting justify-content on the wrong element — Symptom: nothing moves, items ignore the property completely — Fix: justify-content and align-items always go on the PARENT (the flex container), never on the child items. If you wrote it on a div that doesn't have display: flex, CSS silently ignores it with zero error messages.
- ✕Mistake 2: Forgetting that flex-direction: column swaps the axes — Symptom: justify-content: center doesn't centre items vertically as expected — Fix: When flex-direction is column, justify-content moves things UP and DOWN (the main axis is now vertical), and align-items moves things LEFT and RIGHT. The properties don't change — the axes flip. Draw the axes on paper before debugging.
- ✕Mistake 3: Using flex without flex-wrap on card grids — Symptom: On small screens, all cards squish into a single overflowing row instead of wrapping — Fix: Add flex-wrap: wrap to the container and set a minimum width on items with flex: 1 1 250px. Without flex-wrap: wrap the default is nowrap, meaning Flexbox will shrink items indefinitely rather than wrap them to the next row.
Interview Questions on This Topic
- QWhat's the difference between justify-content and align-items in Flexbox, and what happens to each when you change flex-direction to column?
- QHow would you build a navigation bar where the logo is on the left and the nav links are on the right, using only Flexbox? Walk me through your approach.
- QWhat does flex: 1 actually expand to, and why would you use flex: 0 0 200px on a sidebar — what does each value mean and what behaviour does it produce?
Frequently Asked Questions
When should I use Flexbox instead of CSS Grid?
Use Flexbox when your layout flows in one direction — a row of buttons, a navigation bar, or a vertical list of cards. Use CSS Grid when you need to control both rows and columns at the same time, like a full page layout or a photo gallery. In practice, most real projects use both: Grid for the page skeleton, Flexbox inside components.
Why isn't my justify-content: center working?
The most likely cause is that you put justify-content on the flex item instead of the flex container. Check that the element with justify-content also has display: flex on it. The second common cause is that your container has no defined width or height, so there's no extra space to distribute — add a width or height to the container and the alignment will kick in.
What's the difference between align-items and align-content?
align-items aligns flex items within a single row on the cross axis — it works even when everything is on one line. align-content only does anything when you have flex-wrap: wrap enabled AND items have actually wrapped onto multiple rows — it then controls how those rows of items are spaced relative to each other within the container. If your items aren't wrapping, align-content has zero effect.
Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.