Most teams do not have a Grid problem or a Flexbox problem. They have a layout relationship problem. The fastest way to make the right choice is to name the relationship before writing the declaration.
If rows and columns both matter, start with Grid. If the layout distributes items along one axis, start with Flexbox. If a child layout must align to tracks owned by a parent, consider Subgrid. If the component needs to change based on its allocated space instead of the viewport, add container queries.
The decision test
Ask what would make the layout wrong if the content changed.
- If cards must line up in both rows and columns, you need a grid.
- If buttons should wrap and keep natural widths, Flexbox is usually enough.
- If a nested heading and body text must align with sibling cards, Subgrid can remove duplicate track math.
- If the same component appears in a sidebar and a main column, viewport media queries may be the wrong boundary.
This matters because layout bugs often come from solving the wrong relationship. A toolbar written with Grid can become rigid. A dashboard written only with Flexbox can lose column alignment. A reusable card tuned with viewport breakpoints can be wrong in every container except the one where it was designed.
Grid is for shared structure
Grid is strongest when the parent owns a two-dimensional structure. You can name areas, size tracks, align items, and let auto-placement fill predictable slots.
.dashboard {
display: grid;
grid-template-columns: minmax(14rem, 18rem) minmax(0, 1fr);
grid-template-areas:
"nav header"
"nav main";
min-block-size: 100svh;
}
The minmax(0, 1fr) detail is not decoration. It tells the flexible track that it is allowed to shrink below the min-content size of its children. Without it, a long table, code sample, or unbroken URL can push the layout wider than the viewport.
Flexbox is for distribution
Flexbox is strongest when items need to size from their content and distribute remaining space along one axis.
.toolbar {
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
align-items: center;
}
.toolbar__spacer {
margin-inline-start: auto;
}
This works because the toolbar is not asking items to share vertical tracks. It is asking them to sit in a row, wrap when needed, and preserve natural button sizes.
The production rule
Choose the primitive that describes the invariant. If the invariant is “these columns align,” use Grid. If the invariant is “these controls flow together,” use Flexbox. If the invariant is “this component responds to its own width,” use a container query. That keeps the CSS readable when the next developer has to debug the layout six months later.