Start with the layout’s dimensions
Choose Grid when both rows and columns matter. Choose Flexbox when one axis does most of the work.
With Grid, you define a structure such as three columns and two rows, then decide where content belongs. With Flexbox, you give a container some items and describe how they should use the available space. The items’ intrinsic sizes remain part of that calculation.
If you know the shape of the layout before its content arrives, Grid is a good starting point. If you have a row or column of items with variable sizes, Flexbox is often the simpler fit.
| Grid | Flexbox | |
|---|---|---|
| Axes | Two at once | One at a time |
| Driven by | The container’s definition | The items’ content |
| Overlapping items | Yes, via grid areas | No |
| Alignment across rows | Columns line up automatically | Each line is independent |
| Good fit for | Page structure, dashboards, card grids, forms | Navbars, toolbars, button groups, tag lists, centring |
Check whether separate rows must align
This is a useful test when the choice is not obvious. A wrapped Flexbox container lays out each line independently, so the items on one row do not have to align with the next row. Grid defines its columns once and uses them for every row.
A set of equal-width cards usually belongs in Grid. A row of filter pills that should wrap according to their labels usually belongs in Flexbox. Flexbox can make a card layout, but its independent lines often leave the last row looking unrelated to the rows above it.
Useful layout patterns
Responsive card grid with no media queries
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 1rem;
}
Each column is at least 260px wide, and the available columns share the remaining space equally. The count changes with the viewport without a breakpoint or JavaScript. auto-fill retains empty tracks when there are only a few items; auto-fit collapses those tracks and lets the existing items stretch.
Sidebar that collapses on narrow screens
.layout {
display: grid;
grid-template-columns: minmax(0, 1fr) 320px;
gap: 3rem;
}
@media (max-width: 1040px) {
.layout { grid-template-columns: minmax(0, 1fr); }
}
Full page layout with named areas
.page {
display: grid;
min-height: 100dvh;
grid-template-rows: auto 1fr auto;
grid-template-columns: 240px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
.page > header { grid-area: header; }
.page > nav { grid-area: sidebar; }
.page > main { grid-area: main; }
.page > footer { grid-area: footer; }
@media (max-width: 768px) {
.page {
grid-template-columns: 1fr;
grid-template-areas: "header" "main" "sidebar" "footer";
}
}
Named areas make the page structure visible in the stylesheet. For a narrow screen, you can rewrite the template instead of overriding each item’s position. The example uses 100dvh rather than 100vh so the height responds to mobile browser controls appearing and disappearing.
Navbar: logo left, links right
.nav {
display: flex;
align-items: center;
gap: 1rem;
}
.nav-links {
margin-left: auto; /* uses the remaining space */
}
On a flex item, margin-left: auto consumes the free space before that item and pushes it, along with anything after it, to the end. This stays predictable when the navigation contains more than two groups.
Footer pinned to the bottom on short pages
body {
min-height: 100dvh;
display: flex;
flex-direction: column;
}
main { flex: 1; } /* fills the remaining height */
Centring
/* Both approaches center the content. */
.centre { display: grid; place-items: center; }
.centre { display: flex; align-items: center; justify-content: center; }
Common trouble spots
Using margins instead of gap
/* Requires a trailing margin override. */
.item { margin-right: 1rem; }
.item:last-child { margin-right: 0; }
.container { display: flex; gap: 1rem; }
Flexbox gap has had broad browser support since 2021. It applies only between items, which avoids trailing space and removes the need for a :last-child override.
Not understanding flex: 1
flex: 1; /* equal widths with a zero basis */
flex: auto; /* starts at the natural width, then grows */
flex: none; /* prevents growing and shrinking */
The important difference between flex: 1 and flex: auto is the basis. A basis of 0% ignores the content’s width before distributing space, while auto starts from the natural width. Use flex: 1 when equal shares matter more than intrinsic size.
Flex items overflowing their container
A flex item defaults to min-width: auto, so it will not shrink below the width its content requires. A long unbroken string or wide table can then push the whole container wider.
.flex-child {
min-width: 0; /* permits shrinking below the content width */
overflow-x: auto; /* scrolls content that still overflows */
}
For unexplained horizontal scrolling, inspect min-width on flex children and the minimum value of Grid tracks before changing the outer container.
Keep visual and document order together
Flexbox order and explicit Grid placement change only the visual order. Screen readers still announce the DOM order, and keyboard focus follows it as well.
Subgrid
Subgrid lets a nested grid inherit the tracks of its parent. That makes it possible to align content inside neighboring cards even when their titles and body text have different lengths.
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 1rem;
}
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid; /* aligns each card section across the row */
}
Current versions of the major browsers have supported Subgrid since late 2023. For older browser versions, use a fixed min-height on the title or make each card a flex column and apply margin-top: auto to its footer.
Frequently asked questions
Should I use Grid or Flexbox?
Start with Grid when you are defining rows and columns and placing content within them. Start with Flexbox when you are distributing a row or column of items whose sizes vary. It is normal to use Grid for page structure and Flexbox inside individual components.
Is Grid slower than Flexbox?
The performance difference is not meaningful for normal layouts. Browsers implement both natively. Choose the module that describes the layout clearly; repeated layout reads and writes or forced reflows are more likely sources of a rendering problem.
Can I nest Grid inside Flexbox and vice versa?
Yes. A flex navigation bar can contain a Grid-based dropdown, and a Grid cell can contain a flex toolbar. Each container establishes its own formatting context, so nesting them is routine.
Why is my grid item overflowing its column?
A 1fr track has an automatic minimum, so wide content can force the track to grow. Try minmax(0, 1fr), then add overflow-x: auto to the child that should scroll. For a Flexbox child, the corresponding fix is min-width: 0.
What is the difference between auto-fill and auto-fit?
auto-fill preserves empty tracks, so three cards in a six-column grid remain at their track width. auto-fit collapses empty tracks and allows those three cards to fill the row. The choice depends on whether a small number of items should stretch.
Do I still need media queries?
Yes, but intrinsic sizing can remove some of them. minmax with auto-fill handles many responsive grids without a breakpoint. Container queries cover another common case by letting a component respond to its own width instead of the viewport.