CSS Grid and Flexbox: when to use each (and how to use both)

Flexbox works on one axis. CSS Grid works on two. Learn the decision rule, core properties, and the no-media-query responsive pattern.

Published 12 min read
Fredy RodriguezFredy Rodriguez
Overhead flat-lay of blank modular rectangular panels arranged in a precise two-dimensional grid on concrete, illustrating CSS Grid two-axis and Flexbox one-axis layout structure
Table of Contents

Before CSS Grid and Flexbox existed, web developers built layouts by misusing HTML tables, then by misusing the CSS float property. Both approaches worked, technically, but they required careful hackery: clearfix utilities, calculated widths, and a permanent sense that the browser was fighting the intention.

CSS Grid and Flexbox ended that era. They are purpose-built layout systems, and they arrived with a specific division of labor that, once understood, makes most layout problems straightforward. This post explains what each system does, when to reach for each one, and how to combine them in a real project.

Why CSS layout finally makes sense

Floats, table-based layouts, and the problems they created

HTML tables were designed for tabular data, not page structure. Using them for layout worked visually but produced markup that was rigid, inaccessible to screen readers, and difficult to adapt for different screen sizes.

float was intended to let text wrap around images. Developers repurposed it for multi-column layouts, which required adding an invisible “clearfix” element or pseudo-element after every floated container to prevent the parent from collapsing. It worked until it didn’t, and debugging float-based layouts was a slow process.

How Flexbox and Grid solved different parts of the problem

Flexbox, introduced to browsers between 2012 and 2014, solved component-level alignment: distributing items in a row or column, centering things, spacing buttons evenly, wrapping a row of cards. It made many of the most common float patterns obsolete.

CSS Grid, which shipped simultaneously across Chrome 57, Firefox 52, and Safari 10.1 in March 2017, solved page-level two-dimensional layout: defining rows and columns explicitly and placing elements into them with precision. Both systems have had full cross-browser support with no vendor prefixes for years. Flexbox reached Baseline “Widely Available” status in 2018. CSS Grid Level 1 reached the same threshold in 2020.

Why both exist and why neither replaces the other

The distinction is not about which system is more modern or more capable. They solve different geometric problems. According to MDN Web Docs, “the basic difference between CSS Grid layout and CSS Flexbox layout is that Flexbox was designed for layout in one dimension (either a row or a column). Grid was designed for two-dimensional layout: rows, and columns at the same time.”

That sentence is the whole decision tree, condensed.

Flexbox: one-dimensional layout

What Flexbox controls

Flexbox creates a flex container. Every direct child of that container becomes a flex item. The container distributes items along a main axis (horizontal by default) and aligns them on the cross axis (vertical by default). Each line of items sizes itself independently based on its own items and available space.

The key word is “one-dimensional.” Flexbox works on a row or a column. When items wrap to a new line, that new line is its own independent flex context: items on line 2 have no alignment relationship to items on line 1.

The core properties

.container {
  display: flex;
  flex-direction: row;        /* row | column | row-reverse | column-reverse */
  justify-content: space-between; /* alignment on the main axis */
  align-items: center;        /* alignment on the cross axis */
  flex-wrap: wrap;            /* allow items to wrap to new lines */
  gap: 1rem;                  /* spacing between items */
}

.item {
  flex: 1;                    /* shorthand for flex-grow flex-shrink flex-basis */
}

justify-content controls spacing along the main axis: flex-start, flex-end, center, space-between, space-around, space-evenly. align-items controls alignment on the cross axis. flex: 1 tells an item to grow and shrink to fill available space equally with its siblings.

What Flexbox is good at

Flexbox is the right tool for:

  • Navigation bars (a row of links, spaced evenly or pushed to each end)
  • Rows of cards where you want equal height within the row
  • Button groups with consistent spacing
  • Centering a single element both horizontally and vertically
  • Any layout that follows a single line or column of items

A one-liner to center anything: display: flex; justify-content: center; align-items: center;

What Flexbox is not built for

Flexbox does not know about the grid of items as a whole. If you have three rows of cards and want the cards in row 1 to align with the cards in rows 2 and 3, Flexbox cannot guarantee that without additional constraints. Each row makes its own sizing decisions. For that kind of full-grid alignment, CSS Grid is the right tool.

CSS Grid: two-dimensional layout

What Grid controls

Grid creates an explicit layout container with named rows and columns. Every direct child of the container becomes a grid item. You define the tracks (rows and columns) first. Items either follow auto-placement rules or are placed explicitly using grid-area, grid-column, and grid-row properties.

The key difference from Flexbox: Grid items in different rows still know about each other. A track change affects the entire column or row, not just one item. This is what makes Grid the right choice for page-level structure and complex content grids.

The core properties

.container {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;  /* three columns: 1/4, 1/2, 1/4 of available width */
  grid-template-rows: auto 1fr auto;   /* three rows: shrink-to-content, flexible, shrink-to-content */
  gap: 1.5rem;                         /* spacing between all tracks */
}

.sidebar {
  grid-column: 1;        /* place in first column */
  grid-row: 1 / 3;       /* span rows 1 through 3 */
}

.hero {
  grid-area: hero;       /* referenced by name from grid-template-areas */
}

grid-template-columns defines the column structure. gap (formerly grid-gap) sets the gutter between tracks. grid-column and grid-row place an item across explicit track positions.

The fr unit

fr stands for “fractional unit” and it is Grid’s most powerful feature for responsive layouts. One fr is one share of the available space after fixed-size tracks are subtracted. 1fr 2fr 1fr creates three columns where the middle column is twice as wide as each outer column, and all three adapt proportionally to the container width.

fr units cannot go below their content size by default: a track with 1fr will not shrink smaller than its smallest item. For truly flexible tracks that can shrink to zero, minmax(0, 1fr) is the correct syntax.

What Grid is good at

CSS Grid excels at:

  • Full-page layouts (header, sidebar, main content, footer positioned simultaneously)
  • Card grids where items must align in both rows and columns
  • Asymmetric designs where items span multiple tracks
  • Dashboard layouts with multiple named areas
  • Any layout where two-dimensional alignment matters

Grid is also the foundation of mobile-first design, handling the full spectrum from a single-column mobile layout to a multi-column desktop layout with the same rules. For a deeper look at how this plays out in practice, see our post on CSS Grid and Flexbox as the foundation of mobile-first layouts.

When to use Flexbox vs. CSS Grid

The decision rule: one axis or two?

This is the question that resolves most layout decisions quickly:

  • One axis: You are distributing items in a row or a column and you do not need the cross-axis rows to align with each other. Use Flexbox.
  • Two axes: You need items to align both horizontally and vertically as a grid. Use CSS Grid.

Another way to think about it: is the layout driven by the content or by the container? Flexbox is content-out: items determine how space is divided. Grid is layout-in: the container defines the tracks and items fill them.

Practical examples from real web layouts

Navigation bar: Flexbox. Links arranged in a row, spaced evenly or pushed to opposite ends. One axis, content-driven.

Product or portfolio card grid: CSS Grid. Cards must align in both rows and columns. Two-dimensional.

Page skeleton (header, sidebar, main, footer): CSS Grid. The structural layout of the whole page is a two-dimensional problem.

Card component internals (icon + title + body text): Flexbox. Inside the card, you are aligning items vertically in a column or horizontally in a row.

Centered hero section: Either works. Flexbox justify-content: center; align-items: center is common and readable.

Nesting: how Flexbox inside Grid solves complex layouts

The most practical pattern is Grid for the outer structure and Flexbox for the inner components. An event site like Super Lap Battle might use Grid to define the page skeleton and the card grid for race classes, then use Flexbox inside each card to align the sponsor logo, event title, and registration button. The two systems do not compete. They nest.

A video studio portfolio like Motion Giraffx applies the same pattern: Grid positions the portfolio sections and thumbnail grid, Flexbox aligns metadata and action links within each card.

Building a responsive layout without media queries

auto-fill and auto-fit with minmax()

Grid’s repeat() function accepts auto-fill or auto-fit as the track count, which tells the browser to create as many columns as will fit given a minimum track size. Combined with minmax(), this produces a fully responsive grid with a single CSS declaration.

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 1.5rem;
}

This creates as many columns as fit in the container, each at least 15rem wide, each growing to fill available space equally. On a narrow screen, one or two columns appear. On a wide screen, four or five appear. No media queries.

auto-fill vs. auto-fit

Both create the maximum number of tracks that fit. The difference appears only when there are fewer items than available track slots:

  • auto-fill keeps empty tracks at their minimum size. If three cards exist but five track slots fit, two empty slots remain. Items do not stretch to fill the row.
  • auto-fit collapses empty tracks to zero width. The existing items expand via 1fr to fill all available space. Three cards stretch to fill the full width.

For most card grids, auto-fill produces the more expected result: cards hold a consistent size and new cards drop into consistent slots. auto-fit is useful when you want a small number of items to always fill the full container width.

This pattern eliminates the majority of breakpoint-based media queries for grid layouts. When specific behavior at named breakpoints is needed (hiding a sidebar, changing the number of columns at exact widths), media queries remain the right tool, but for fluid card grids, minmax() is more expressive and simpler to maintain.

CSS Grid and Flexbox in a design system context

Layout decisions compound across a project. A component library for a growing Houston business, whether a multi-location service in the Galleria area or a Medical Center practice, benefits from consistent layout primitives that use Grid and Flexbox deliberately rather than ad hoc.

A Stack utility (display: flex; flex-direction: column) spaces elements vertically. A Cluster utility (display: flex; flex-wrap: wrap) groups inline elements. A Grid utility handles card and content grids. Each primitive covers a distinct layout need, and together they reduce the custom CSS any given component requires.

The gap property works in both Flexbox and Grid. Using it with spacing tokens from a design system (a scale of 0.5rem, 1rem, 1.5rem, 2rem) keeps every layout consistent. One practical rule: gap spaces items within a container. Use padding to space the container from its own edges. Mixing margin into Grid or Flexbox children introduces compounding spacing that is harder to maintain.

This connects directly to the development phase of any web project, as covered in how CSS layout techniques fit into the development phase. Clean, CSS-only layouts are also lightweight by nature. For context on how layout choices interact with JavaScript bundle performance, see our post on code splitting and lazy loading.

What to learn next after Flexbox and Grid

CSS Grid and Flexbox are the current layout baseline for professional web development. After getting comfortable with both, the natural next step is CSS Subgrid (now supported across all major browsers as of March 2026), which lets a nested grid item align to its parent’s track lines rather than creating new ones.

Beyond that, Cascade Layers (@layer) and container queries are the two CSS features most actively changing how component-level styles are written in 2025 and 2026.

For a technical team evaluating a new developer, asking “when would you use Grid over Flexbox?” is one of the most efficient screening questions available. The answer reveals whether they understand the layout model or are guessing at CSS until it works.

These are the foundations that every custom website we build is written with. If you want to see how modern CSS layout principles translate into a real project, take a look at our work or reach out to start a conversation.

Frequently asked questions

When should I use CSS Grid vs Flexbox?

Use Flexbox when you need to control layout along a single axis: a row of navigation links, a row of cards, a centered button group. Use CSS Grid when you need to control rows and columns at the same time, such as a full-page layout or a card grid where items need to align both horizontally and vertically. The short rule: one axis means Flexbox, two axes means Grid.

What is the difference between CSS Grid and Flexbox?

Flexbox is a one-dimensional layout system: it distributes items along either a row or a column, and each line of items sizes itself independently. CSS Grid is a two-dimensional layout system: you define rows and columns explicitly, and items can be placed to span across both dimensions simultaneously. This means Flexbox items on different rows have no alignment relationship to each other, while Grid items on different rows do.

Is CSS Grid better than Flexbox?

Neither is better. They solve different problems. CSS Grid replaced the need for the most complex layout hacks, and Flexbox replaced float-based component layouts. Today most well-built sites use both: Grid for the page-level structure and Flexbox for component-level alignment inside those Grid areas. Choosing one over the other for everything is the wrong framing.

How do I make a responsive layout with CSS Grid?

The standard pattern uses repeat(), auto-fill or auto-fit, and minmax(). For example: grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)) creates as many columns as fit at a minimum of 15rem each, expanding to fill available space. No media queries required. Use auto-fit instead of auto-fill if you want fewer items to stretch and fill the full row width.

Can I use Flexbox and CSS Grid together?

Yes, and you should. The most common pattern is Grid for the outer page or section structure and Flexbox for the component-level details inside each Grid area. A card component, for example, might be positioned by Grid but use Flexbox internally to align its icon, heading, and body text. Nesting the two systems is normal and produces the most maintainable layout code.

Is Flexbox still relevant?

Flexbox is fully relevant and in active use across the web. It has had cross-browser Baseline support since 2018 and remains the right tool for one-dimensional layout tasks. CSS Grid did not replace Flexbox. It added a second tool for two-dimensional layout. Navigation bars, card rows, button groups, and most component-level alignment are still best handled with Flexbox.

Share this postTwitter / XLinkedInBluesky
Fredy Rodriguez
Fredy Rodriguez

Technical Director & Co-Founder

Runs the data-and-code side of Desque: SEO, GEO, AEO, PPC, copywriting, and the engineering behind every site we ship. Builds in Go and TypeScript.