CSS Flexbox vs Grid — Which Layout Tool to Reach For
Photo by Pankaj Patel on Unsplash
Table of Contents
- The Question I Couldn't Answer Confidently
- Flexbox: Content-First, One Axis at a Time
- Grid: Layout-First, Two Axes at Once
- The One-Dimensional vs Two-Dimensional Rule (and Its Limits)
- Side-by-Side: Code Comparison for Real Layouts
- Alignment Properties: The Part That Actually Takes Practice
- Browser Support and What You Can Actually Use
- FAQ
The Question I Couldn't Answer Confidently
So I went home and built a few layouts both ways. Turned out I'd been using Flexbox for layouts that really wanted to be Grid, and avoiding Grid for things it handles effortlessly. The one-dimensional vs two-dimensional rule isn't wrong, but it's not the full picture. The question I should've been asking is: does the layout follow the content, or does the content follow the layout?
Once I started thinking about it that way, it clicked. And I haven't agonized over the choice since.
Flexbox: Content-First, One Axis at a Time
Photo by Sigmund on Unsplash
That's the mental model: you have a flex container, and the children decide their own sizing (with guidance from `flex-grow`, `flex-shrink`, and `flex-basis`). The container distributes leftover space. This makes Flexbox phenomenal for:
- Navigation bars where you want some links on the left and some on the right - Card components where you want items evenly spaced with `justify-content: space-between` - A single row or column of items that should stretch or shrink based on available space - Centering a single element both horizontally and vertically (the classic `display: flex; align-items: center; justify-content: center` trick) - Form layouts where labels and inputs sit side by side
Here's a navigation bar in Flexbox — probably the most common pattern I write:
```css .nav { display: flex; justify-content: space-between; align-items: center; padding: 0 1rem; }
.nav-links { display: flex; gap: 1.5rem; } ```
No widths specified. No grid lines. The nav just distributes its children along the horizontal axis and lets them determine their own sizes. That's Flexbox doing what it does best.
Where Flexbox starts to struggle is when you need items to align across rows. The classic Flexbox gotcha is a card grid where you want every card in a row to be the same height. Flexbox can handle that within a single row (with `align-items: stretch`), but if items wrap to a second row, the two rows don't know about each other. A very tall card in row one won't cause cards in row two to match that height.
Grid: Layout-First, Two Axes at Once
Photo by Christopher Gower on Unsplash
This makes Grid perfect for:
- Full page layouts (header/sidebar/main/footer) - Card grids where you want uniform column widths and rows that auto-size - Any layout where you need items to align in two dimensions simultaneously - Complex editorial layouts (think magazine pages, dashboards) - Any situation where items need to span multiple columns or rows
Here's a classic three-column card layout in Grid:
```css .card-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; } ```
Three equal columns. Items fill in automatically left to right, top to bottom. Want cards that auto-fill the available space while staying at least 250px wide?
```css .card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1.5rem; } ```
That one pattern — `repeat(auto-fill, minmax())` — replaced most of the responsive layout work I used to do with media queries and percentage widths. Grid just figures it out.
The MDN documentation for CSS Grid is genuinely worth reading. It's one of the better MDN pages and it has visual examples for all the alignment properties, which helps a lot.
Grid also handles gaps differently than Flexbox does. Both now support the `gap` property (which used to be `grid-gap`), but Grid's gap applies to both axes at once, while Flexbox's `gap` only applies between flex items — it doesn't add space around the outside. A small difference, but it trips people up.
The One-Dimensional vs Two-Dimensional Rule (and Its Limits)
Photo by Ilya Pavlov on Unsplash
The real distinction is about intent. Flexbox lets content influence the layout. Grid forces content to fit into a predefined layout. Both are fine approaches — the question is which one your design needs.
Consider a row of buttons. You could use Grid (`grid-template-columns: repeat(4, auto)`) and it'd work fine. But Flexbox is more natural because you don't care about a specific column count — you just want the buttons in a row with some spacing, and you want them to wrap gracefully if there's not enough room. Content drives the layout.
Now consider a pricing table with three columns that need to be identical width, with rows that align across all three columns. You definitely want Grid. You're defining the layout first — three equal columns — and then filling in the content.
Here's a test I run in my head: if I add or remove an item, should the layout stay the same shape (Grid) or should the layout adapt to fit the new item count (Flexbox)? A navigation menu that should always span full width: Grid. A list of tags that should wrap based on how many there are: Flexbox.
Both can also be nested inside each other, which is how most real layouts work. The outer structure uses Grid. Individual components inside the grid cells use Flexbox for their internal arrangement. That's not a hack — it's exactly how the specs intend them to be used together.
If you're building out a design system alongside your layout work, it's worth having solid CSS utility tools nearby — things like the CSS gradient generator for backgrounds or the CSS box shadow generator for adding depth to card components.
Side-by-Side: Code Comparison for Real Layouts
**Centering a single element:**
```css /* Flexbox */ .container { display: flex; align-items: center; justify-content: center; height: 100vh; }
/* Grid */ .container { display: grid; place-items: center; height: 100vh; } ```
Both work. `place-items: center` in Grid is actually shorter. I've switched to Grid for this one.
**A sidebar + main layout:**
```css /* Flexbox */ .page { display: flex; } .sidebar { width: 250px; flex-shrink: 0; } .main { flex: 1; }
/* Grid */ .page { display: grid; grid-template-columns: 250px 1fr; } ```
Grid wins here on readability. The column definition makes the intent clearer.
**Equal-height cards that wrap:**
```css /* Flexbox — close but not quite right */ .cards { display: flex; flex-wrap: wrap; gap: 1rem; } .card { flex: 1 1 300px; } /* Min 300px, grows to fill */
/* Grid — cleaner and handles rows properly */ .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1rem; } ```
The Flexbox version works but cards in the last row might be different widths if the row isn't full. The Grid version always fills all columns evenly.
If you want to test these patterns live, you can paste CSS into ToolsFuel's CSS tools and preview changes without setting up a local project.
Alignment Properties: The Part That Actually Takes Practice
In Flexbox, the axis matters: - `justify-content` aligns items along the **main axis** (the direction of flex — row or column) - `align-items` aligns items along the **cross axis** (perpendicular to flex direction)
So `justify-content: center` in a row flex container centers items horizontally. But switch to `flex-direction: column` and the same property centers items vertically. The axes flip.
In Grid, the mental model is simpler: - `justify-*` properties always control the horizontal (inline) axis - `align-*` properties always control the vertical (block) axis
No matter what your grid layout is, `justify-items` moves things left/right and `align-items` moves things up/down. I find Grid's alignment model more predictable once you're doing complex layouts.
Both support the `gap` property for spacing. Both support `order` to rearrange items visually without changing DOM order (useful for accessibility — you can have a visually different order than the screen reader order).
And both support `auto` margins, which is underused. `margin-left: auto` on a flex item pushes everything after it to the right side. It's how I build nav bars with logo on left and links on right without explicit positioning:
```css .nav { display: flex; align-items: center; } .logo { /* stays on left */ } .nav-links { margin-left: auto; /* pushed to right */ display: flex; gap: 1rem; } ```
Clean, simple, and it works without any fixed widths or absolute positioning.
Browser Support and What You Can Actually Use
Flexbox has been supported in all major browsers since around 2015. Grid followed in 2017, and the more advanced features like `subgrid` (which lets child grids align to their parent grid lines) became widely supported in late 2023 after Firefox had it for years before other browsers caught up.
Subgrid is worth knowing about. It solves a genuinely annoying problem: you've got a grid with cards, and each card has a title and description. You want the titles to align across cards regardless of how long each description is. Without subgrid you'd need JavaScript to equalize heights. With subgrid the CSS handles it:
```css .card-grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: auto; } .card { display: grid; grid-row: span 3; /* title, body, footer */ grid-template-rows: subgrid; } ```
Subgrid is one of those features I should've been using for the past year and wasn't. If you're building any kind of card component with multiple internal sections that need to align across siblings, it's worth the five minutes it takes to learn.
The CSS generator tools at ToolsFuel can help you build grid templates visually if you're still getting comfortable with the syntax — sometimes seeing the output in real time is faster than mentally working through column repeat notation.
Frequently Asked Questions
Should I just use CSS Grid for everything?
Grid can technically handle most layout tasks, but that doesn't mean you should reach for it every time. Flexbox is genuinely better for one-dimensional layouts where you want content to drive sizing — things like navigation bars, button groups, and tag lists. Grid is better when you're defining a structure first and filling it in. Most real-world layouts use both: Grid for the page structure, Flexbox for components inside the grid cells. Don't pick one and commit to it for everything.
Does Flexbox work on mobile browsers?
Yes, completely. Flexbox has had near-universal mobile browser support since around 2015. You don't need any vendor prefixes or fallbacks for current browser targets. Same for CSS Grid — it's been supported across mobile Safari, Chrome for Android, and Firefox Mobile for years. If you're still writing -webkit-flexbox or -ms-flexbox prefixes, you can drop those unless you're supporting browsers from before 2015.
What is the difference between align-items and justify-content?
In Flexbox, justify-content controls alignment along the main axis (the direction your flex items flow — horizontal if flex-direction is row, vertical if it's column), while align-items controls the cross axis (perpendicular to the flow direction). In Grid, the convention is simpler: justify-* always controls horizontal alignment and align-* always controls vertical alignment, regardless of layout. The most common confusion is in Flexbox when you switch flex-direction to column — suddenly justify-content controls vertical spacing and align-items controls horizontal, which is the opposite of the default row behavior.
Can I use Flexbox inside a Grid container?
Yes, and you should. Nested layouts are how real-world pages work. A common pattern is using CSS Grid for the overall page structure — header, sidebar, main content area, footer — and then using Flexbox inside individual components for their internal layout. The two systems are completely compatible. There's no performance penalty for nesting them, and it's considered standard practice. Each grid cell or flex item can itself be a flex or grid container.
What is CSS subgrid and when do I need it?
Subgrid lets a nested grid element participate in its parent grid's track sizing, so child items can align to the same row and column lines as the parent. It's most useful when you have a grid of card components where each card has multiple internal sections (like a header, body, and footer) that should line up across all cards in the same row. Without subgrid you'd either use JavaScript to equalize heights or accept misaligned content. With subgrid, the CSS handles it cleanly. Browser support for subgrid became solid in late 2023 across Chrome, Safari, and Firefox.
Where can I practice writing CSS Flexbox and Grid layouts?
ToolsFuel has [CSS tools](/tools) that let you experiment with CSS properties without a full project setup. For specific practice, CSS-Tricks' Complete Guide to Flexbox and Complete Guide to Grid are excellent references. There are also games like Flexbox Froggy and Grid Garden that teach the alignment properties through puzzles — I'm not embarrassed to say I still use Grid Garden occasionally when a subgrid property isn't behaving the way I expect.
Try ToolsFuel
23+ free online tools for developers, designers, and everyone. No signup required.
Browse All Tools