CSS Grid vs Flexbox in 2026: When to Use Each (With Code)

Use Flexbox for one-dimensional layouts like nav bars, toolbars, or button rows, where content decides sizing along a single axis. Use CSS Grid for two-dimensional layouts like page shells, dashboards, or galleries, where you define rows and columns first. They combine well: Grid for structure, Flexbox inside the components.
Part of the complete web design guide.
On this page
- The mental model that actually sticks
- A real Flexbox example: the nav bar
- A real Grid example: the page shell
- When to use which
- They work together, not against each other
- Newer bits worth using in 2026
- Subgrid
- Gap everywhere
- Logical properties and container queries
- Grid’s newer functions
- My actual decision process
- Conclusion
- FAQ
- Can I use Grid and Flexbox in the same project?
- Is Flexbox still worth learning now that Grid exists?
- Which one is faster or better for performance?
I’ve built a lot of layouts over the years, and the Grid vs Flexbox question still comes up in almost every code review or onboarding session. The short answer: they solve different problems, and the confusion usually comes from people trying to force one to do the other’s job. Here’s how I actually decide, with code you can lift straight into a project.
| Factor | CSS Grid | Flexbox |
|---|---|---|
| Axis | Two-dimensional, rows and columns | One-dimensional, single row or column |
| Who drives size | Container defines the track structure | Content decides its own size |
| Best fit | Page shells, dashboards, card galleries | Nav bars, button rows, tag lists |
| Item placement | Explicit lines, areas, or auto-flow | Order and alignment along one axis |
| Overlap control | Items can stack in the same cell | No native overlap |
| Learning curve | Steeper, more properties to learn | Gentler, fewer moving parts |
The mental model that actually sticks
Flexbox is one-dimensional. You lay out items along a single axis (a row or a column) and the browser distributes space based on the content. It’s content-driven: the items tell the container how big they want to be, and the container negotiates. You reach for it when you have a set of things that should sit next to each other and flow naturally.
Grid is two-dimensional. You define rows and columns up front, then place items into that structure. It’s layout-driven: the container tells the items where they go. You reach for it when you already know the shape of the layout (a page shell, a dashboard, a photo gallery) and you want precise control over both axes at once.
That’s the whole thing. Everything else is detail. When I’m staring at a mockup, I ask one question: am I arranging a single line of stuff, or am I defining a structure? Single line, Flexbox. Structure, Grid.
A real Flexbox example: the nav bar
The classic case. A logo on the left, links on the right, and it needs to hold up when someone adds a link or the logo text gets longer. This is Flexbox territory because it’s one row and the content drives the sizing.
<nav class="nav">
<a class="brand" href="/">Kenny Key</a>
<ul class="links">
<li><a href="/work/">Work</a></li>
<li><a href="/blog/">Blog</a></li>
<li><a href="/contact/">Contact</a></li>
</ul>
</nav>
<style>
.nav {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1.5rem;
padding: 1rem 1.5rem;
}
.links {
display: flex;
gap: 1.25rem;
list-style: none;
margin: 0;
padding: 0;
}
@media (max-width: 600px) {
.nav {
flex-direction: column;
align-items: flex-start;
}
}
</style>
Two things worth pointing out. First, justify-content: space-between pushes the brand and the links apart without any floats or margin hacks. Second, gap replaces the old margin-based spacing tricks. If you’re still writing .links li + li { margin-left: 1.25rem; }, stop. gap works in Flexbox now (it has for a few years across every browser that matters) and it’s cleaner because it doesn’t add space on the outer edges.
The same pattern shows up in a card row. You want cards in a line that wrap when the screen gets narrow. Flexbox with flex-wrap: wrap and a flex: 1 1 280px on the cards does it, and the cards grow to fill leftover space in a way that feels right.
A real Grid example: the page shell
Now the opposite. A page with a header, a sidebar, a main content area, and a footer. This has a defined structure. Grid lets me describe it in one place instead of fighting with floats or a Flexbox column plus nested rows.
<div class="page">
<header>Header</header>
<aside>Sidebar</aside>
<main>Content</main>
<footer>Footer</footer>
</div>
<style>
.page {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
gap: 1.5rem;
min-height: 100dvh;
}
.page > header { grid-area: header; }
.page > aside { grid-area: sidebar; }
.page > main { grid-area: main; }
.page > footer { grid-area: footer; }
@media (max-width: 800px) {
.page {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
}
}
</style>
Look at what grid-template-areas buys you. The layout is readable as a picture in the CSS. The media query reorders the page just by rewriting the area map. No source order gymnastics, no order properties scattered around. If I need the sidebar to move below the content on mobile, I change one string.
A gallery is the other Grid sweet spot. repeat(auto-fill, minmax(220px, 1fr)) gives you a responsive grid that adds and removes columns as the viewport changes, with no media queries at all. Flexbox can approximate this, but it can’t line up the last row into columns without fixed heights and a lot of pain.
When to use which
My honest rules, in order of how often they come up:
- Use Flexbox for components. Nav bars, button groups, card internals (icon + text + action), form rows, toolbars, tag lists. Anything where the content decides the size and it flows in one direction.
- Use Grid for page structure. Header/sidebar/main/footer, dashboards, two-column article layouts, anything with a shape you’d sketch on a napkin first.
- Use Grid for galleries and card grids. When alignment across rows matters, Grid wins. Flexbox wraps unevenly and the last row looks off.
- Use Flexbox when you don’t know the count. A list of tags or a row of avatars that might be three items or thirty. Let the content drive.
- Use Grid when you want overlap. Grid items can occupy the same cell. Stack a caption over an image with
grid-area: 1 / 1on both and no absolute positioning.
One more: if the layout needs to be centered both ways, either works, but Grid’s place-items: center on the container is the shortest path. Flexbox needs justify-content and align-items and the container has to be full height. I’ll usually reach for Grid there.
They work together, not against each other
The framing of “Grid vs Flexbox” is a bit of a trap. In real projects I use both in the same file, usually nested. Grid lays out the page, Flexbox handles the components inside the grid cells.
.page {
display: grid;
grid-template-columns: 240px 1fr;
gap: 1.5rem;
}
.card-row {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.card {
flex: 1 1 260px;
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.card .actions {
margin-top: auto; /* pushes actions to the bottom */
display: flex;
gap: 0.5rem;
}
Notice the nesting. The page is Grid. The card row is Flexbox. Each card is a Flexbox column. The actions inside the card are a Flexbox row. That margin-top: auto on the actions is a Flexbox trick that pins the buttons to the bottom of each card regardless of how much text is above them, which is exactly the kind of content-driven behavior Grid doesn’t do as cleanly.
If you’re hiring out this kind of work, the difference between someone who reaches for the right tool and someone who hacks everything with floats and margins shows up fast in the maintenance cost. It’s a big part of why I care about this stuff on web design projects. A layout that’s built on the right primitives is cheaper to change six months later.
Newer bits worth using in 2026
Subgrid
Subgrid lets a nested Grid inherit the track sizing of its parent. Before subgrid, if you had cards in a grid and you wanted the headings, images, and buttons to line up across all the cards, you had to hardcode row heights or use a fixed grid-auto-rows and hope. Now the child grid can just say grid-template-rows: subgrid and it snaps to the parent’s rows.
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 1.5rem;
}
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid;
}
/* Now every card's title, body, and footer align across the row */
Support is solid in current Chrome, Firefox, and Safari. If you’re on an older browser matrix, feature-query it and fall back, but for most sites in 2026 you can use it.
Gap everywhere
gap works in Flexbox, Grid, and multi-column layouts. It’s the single biggest quality-of-life improvement for layout CSS in the last decade because it kills the margin-collapse and last-child-margin headaches that used to eat an hour every project. Use it. There is no reason to space Flex children with margins in 2026 unless you’re supporting something ancient.
Logical properties and container queries
Not strictly Grid or Flexbox, but they pair with them. Container queries let a component respond to the size of its container instead of the viewport, which means a card can switch from a Flexbox row to a Flexbox column based on the space it actually has. Combine that with gap and subgrid and you get components that adapt without a single viewport media query. This is where I’d spend your learning time if you already know the basics.
Grid’s newer functions
minmax(), repeat(), and auto-fill vs auto-fit are the ones people mix up. auto-fill keeps empty tracks; auto-fit collapses them so items stretch to fill the row. For a gallery where you want the last row to stretch full width, use auto-fit. For a layout where you want consistent column widths even when there are few items, use auto-fill. Small difference, big visual impact.
If you’re doing custom web development and building reusable components, getting comfortable with these is the difference between a component library that bends to new designs and one that breaks every time the mockup changes.
My actual decision process
Sketch the layout. If I can describe it as “a row of things” or “a column of things,” Flexbox. If I can describe it as “a grid with these areas” or “these rows and columns,” Grid. If I’m not sure, I default to Grid for the outer structure and Flexbox for anything inside a cell. That combination covers maybe 95% of what I build.
The other 5% is edge cases: overlapping elements, sticky positioning inside grids, and masonry-style layouts (which still need a workaround, though native masonry support is finally landing in some browsers). For those, I check MDN, test in the browsers I care about, and pick whichever primitive produces the least code.
Don’t overthink it. Both are mature, both work everywhere that matters, and the cost of picking the “wrong” one is usually a minor refactor, not a rewrite. Pick the one that makes the layout read like the design, and move on.
Conclusion
Flexbox is for one-dimensional, content-driven layout: nav bars, card internals, button rows, tag lists. Grid is for two-dimensional, layout-driven structure: page shells, dashboards, galleries. Use Grid for the page and Flexbox for the components inside it, lean on gap for spacing, and reach for subgrid when you need nested items to align across a parent grid. If you learn those four things, you’ll stop guessing and start shipping.
FAQ
Can I use Grid and Flexbox in the same project?
Yes, and you should. They’re not competing standards. The common pattern is Grid for the overall page structure and Flexbox for the components within each grid cell. Nesting them is normal and expected.
Is Flexbox still worth learning now that Grid exists?
Absolutely. Grid can’t do everything Flexbox does. Flexbox handles content-driven sizing, wrapping rows, and one-axis alignment better, and most components you build day to day (nav bars, card headers, form rows) are Flexbox problems. Skipping it would make your CSS worse, not simpler.
Which one is faster or better for performance?
In practice, neither has a meaningful performance edge for typical layouts. The real performance win comes from writing less layout code, avoiding layout thrashing with JavaScript, and not forcing reflows. Choosing the right primitive for the job reduces the CSS you maintain, and that’s the actual speed benefit.


