The CSS gap property: what it actually controls
The gap property has been around for a while, but its behavior depends heavily on the layout context you drop it into. It also gained a significant capability last year: support in Flexbox, alongside CSS Grid. Before that, it was a Grid-only feature. But despite its growing ubiquity, gap isn't as simple as it first appears. Here's a closer look at how it works across layout modes.
The full property family
There are six CSS properties related to gap, though you'll rarely need most of them:
grid-row-gapgrid-column-gapgrid-gaprow-gapcolumn-gapgap
The three grid-* prefixed properties are leftovers from the early CSS Grid specification drafts. They were deprecated when gap was generalized, but browsers still support them, treating them as if the prefix doesn't exist. That means grid-gap is equivalent to gap, grid-column-gap to column-gap, and grid-row-gap to row-gap.
Of the remaining properties, gap is a shorthand for row-gap and column-gap. Understanding what each of those does is the real challenge, and it depends on which layout type you're working with.
Gaps in grid layouts
In CSS Grid, the semantics are straightforward. row-gap inserts space between row tracks, and column-gap inserts space between column tracks.
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 20px;
}
A helpful way to think about gaps in Grid is through grid lines. Those invisible lines that separate tracks have zero thickness by default. When you add a gap, they effectively gain thickness, pushing the tracks apart. Track sizes defined by grid-template-* remain unchanged; the grid simply becomes larger overall.
.container {
gap: 20px; /* same as row-gap: 20px; column-gap: 20px; */
}
/* Switching to row-gap only */
.row-gap-example {
row-gap: 20px; /* vertical space between rows */
}
/* Switching to column-gap only */
.column-gap-example {
column-gap: 20px; /* horizontal space between columns */
}
Grid's row and column directions align with table conventions: columns are vertical, rows are horizontal. That makes it easy to guess where each gap applies, provided you're using the default writing-mode. Should you introduce a vertical writing mode, the axes flip: columns become horizontal and rows vertical. Always check writing-mode before assuming which gap goes where.
Gaps in Flexbox
Flexbox introduces more complexity. Consider a default row-direction flex container:
.container {
display: flex;
flex-direction: row;
column-gap: 20px;
}
Items flow horizontally on a single flex line. column-gap applies between them, and row-gap has no visible effect because there's only one line to work with.
Switch flex-direction to column, and the situation inverts. Items stack vertically, and now row-gap is the property that introduces space between items. column-gap does nothing.
.container {
display: flex;
flex-direction: column;
row-gap: 20px; /* needed instead of column-gap */
}
/* Alternatively */
.container {
display: flex;
flex-direction: column;
gap: 20px; /* applies to both axes; works in both directions */
}
A useful summary: with the default writing mode, column-gap always operates along the vertical axis and row-gap always along the horizontal axis, independent of the flex container's flex-direction.
When wrapping is enabled via flex-wrap: wrap, both gaps become visible in a single layout. Items distribute across multiple flex lines; column-gap spaces items within each line, and row-gap separates the lines themselves. An important difference from Grid: column gaps don't have to align across flex lines. Each line is an independent layout context. If you apply justify-content: center, each line centers its items on its own, and the gaps between items won't line up vertically across different lines.
Gaps in multi-column layouts
Multi-column layout exists for flowing content across vertical columns, similar to a newspaper. Gaps here behave differently from in Grid or Flexbox, in three notable ways:
row-gaphas no effect at allcolumn-gaphas a default value of1em, not0- The gap between columns can be styled
Since there are no rows in a multi-column layout, row-gap is irrelevant. Only column-gap and the gap shorthand apply.
.container {
columns: 3;
column-gap: 2em; /* defaults to 1em if unspecified */
column-rule: 1px solid #ccc; /* style the gap like a border */
}
The default 1em gap is a deliberate choice: even without any explicit gap set, columns remain visually separated. You can override it, but it's a sensible baseline. And unlike in Flexbox or Grid, you can style the empty area between columns with column-rule, which works much like the border property.
What about styling gaps in Flex and Grid?
Styling the actual gap area in Flexbox or CSS Grid—for example, drawing a divider between items—isn't possible today. The CSS Working Group has discussed the idea, and Firefox has work in progress, but no browser supports it as of this writing.
One workaround that gets close: give the container a background color, put a different background color on items, and use gap to expose the container's color between them. But this changes the semantics of the gap; it effectively acts as a border width, so you'd still need padding or margin on items to create real visual separation beyond the colored lines.
Why not just use margins?
Margins are a valid approach to spacing, but gap offers clear advantages in many cases.
The most important difference: gap is defined once, at the container level. Margins require declarations on every individual item, which is messy when items come from different components or have varying spacing rules. Gaps also handle edges correctly. With margin: 0 20px on each item, you get spacing on the outer edges of the container in addition to between items. With gap: 40px, space appears only between items, requiring no special cases for first and last children.
.flex-item {
margin: 0 20px; /* adds outside spacing too */
}
.container {
gap: 40px; /* neat and even spacing between items only */
}
In Grid, the same logic holds. Applying margin: 20px to each item places its own margin around edges of the grid itself; container-level gap: 40px spaces tracks cleanly without edge artifacts.
Combining gap with other spacing mechanics
Margins and gaps aren't mutually exclusive. They can coexist, and often do. A flex or grid container can have both a defined gap and item margins. These stack cumulatively.
If you want to dissect the total space between items, here's what's typically contributing:
- Gap establishes the minimum separation between items; alignment can create more, but never less.
- Margin adds space on all sides of each item, stacking on top of the gap toward the interior.
- Padding contributes space inside each box's boundary.
- Content distribution (via
justify-contentor similar) only kicks in when there's free space left; values likespace-aroundadd their own slot of space around items.
Checking whether a gap is actually working
Debugging gaps has gotten easier in modern browser dev tools, though the techniques differ by browser.
Writing gap on a block container is valid CSS but does nothing:
.container {
display: block;
gap: 20px; /* accepted, but no visual effect */
}
Firefox's Inactive CSS feature detects this and shows a warning in its Inspector, making it clear you've applied a property that has no effect in that layout context.
Chrome and Microsoft Edge take a different route, developed jointly between Google and Microsoft. Hover over the gap property in the Styles panel, and the browser highlights exactly where the space is being generated on the page. This makes it far easier to verify which axis your row-gap or column-gap is actually targeting.
Gaps are well-supported across browser engines, though there are differences to note. Flexbox and Grid support is broad, with the notable exception of Internet Explorer and a few smaller mobile browsers. Multi-column support lags slightly, primarily due to missing support in Safari. In most real-world cases, you can rely on them without workarounds—provided you remember which gap applies where.



