The CSS Gap Property

The gap property is one of those CSS features that feels like it should’ve existed forever: it creates spacing between items in a layout, without you having to sprinkle margins everywhere like parsley.

In this tutorial, you’ll learn exactly how gap works, where it works (grid, flex, and friends), and how to debug the classic “why is my gap doing absolutely nothing?” moment.

What gap does (and where it works)

gap sets the spacing between items in a container layout.

  • Grid: gap controls the spacing between grid tracks.
  • Flexbox: gap controls the spacing between flex items (including wrapped lines).
  • Multi-column layout: column-gap controls spacing between columns (different layout model, similar idea).

Important idea: gap applies to the container, not the items. You add gap on the parent that has display: grid or display: flex.

gap vs margin: what’s the difference?

You can often fake spacing with margins, but gap is built for layout spacing:

  • No “extra space on the edges”: margins can create unwanted spacing at container boundaries unless you carefully remove it for the first/last item.
  • Wrap-friendly: with flex-wrap, margins can get weird (especially vertically). Gap is consistent between items and rows.
  • Cleaner CSS: spacing is owned by the layout container, not each child.

Think of it like this: margins are an item’s personal space; gap is the container’s seating plan.

Grid gap: the simplest way to see it

Grid is the easiest place to understand gap. We’ll start with a 3-column grid and live-edit the gap value.



.grid {
gap: 16px;
} 


*,
::before,
::after {
box-sizing: border-box;
}

.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
padding: 14px;
border: 3px solid #111;
border-radius: 14px;
background: #f6f6f6;
max-width: 720px;
}

.card {
border: 2px solid #111;
border-radius: 12px;
background: #fff;
padding: 14px;
font-family: ui-sans-serif, system-ui, sans-serif;
}

.card strong {
display: block;
margin-bottom: 6px;
}

.card p {
margin: 0;
opacity: 0.85;
} 


Card 1

Gap adds spacing between items.

Card 2

No margins needed.

Card 3

Just a container rule.

Card 4

Try setting gap to 0.

Card 5

Now crank it up.

Card 6

Nice and predictable.

Notice how the spacing appears between the cards, but the outer edge of the grid stays tight because we controlled the container padding separately.

row-gap and column-gap (and the 2-value gap shorthand)

Sometimes you want different spacing vertically vs horizontally. That’s what row-gap and column-gap are for.

  • row-gap controls spacing between rows (block direction).
  • column-gap controls spacing between columns (inline direction).
  • The shorthand gap can take two values: gap: row column;

Let’s live-edit row-gap and column-gap separately.



.grid {
row-gap: 18px;
column-gap: 30px;
} 


*,
::before,
::after {
box-sizing: border-box;
}

.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
padding: 14px;
border: 3px solid #111;
border-radius: 14px;
background: #f6f6f6;
max-width: 780px;
}

.tile {
border: 2px solid #111;
border-radius: 12px;
background: #fff;
padding: 12px;
font-family: ui-sans-serif, system-ui, sans-serif;
text-align: center;
} 


1
2
3
4
5
6
7
8

If you prefer shorthand, the following is equivalent:

  • gap: 18px 30px; means row gap is 18px and column gap is 30px.
  • gap: 20px; means both row and column gaps are 20px.

Flexbox gap: yes, it works (and it’s glorious)

Flex layouts often used margin hacks for spacing. With gap, your flex container becomes the single source of truth for spacing.

In this playground, we’ll use a wrapped flex row and edit the gap live.



.chips {
gap: 12px;
} 


*,
::before,
::after {
box-sizing: border-box;
}

.chips {
display: flex;
flex-wrap: wrap;
padding: 14px;
border: 3px solid #111;
border-radius: 14px;
background: #f6f6f6;
max-width: 760px;
font-family: ui-sans-serif, system-ui, sans-serif;
}

.chip {
border: 2px solid #111;
background: #fff;
border-radius: 999px;
padding: 10px 14px;
line-height: 1;
} 


HTML
CSS
Flexbox
Grid
Selectors
Animations
Layout
Accessibility
Performance

Because this container uses flex-wrap: wrap, the items form multiple lines. Gap keeps spacing consistent between items and between the lines. That’s one of the biggest wins over margin hacks.

Gap works only if you use the right layout mode

Here’s the number one reason people say “gap not working”: they set gap on a container that is still display: block.

Let’s prove it with radios. Switch the container’s display value and watch what happens.

display:


.container {
display: block;
gap: 18px;
} 


*,
::before,
::after {
box-sizing: border-box;
}

.container {
padding: 14px;
border: 3px solid #111;
border-radius: 14px;
background: #f6f6f6;
max-width: 720px;
font-family: ui-sans-serif, system-ui, sans-serif;
}

.container {
grid-template-columns: repeat(3, 1fr);
}

.item {
border: 2px solid #111;
border-radius: 12px;
background: #fff;
padding: 14px;
text-align: center;
} 


One
Two
Three
Four
Five
Six

When display: block, the children are just normal block elements stacked vertically, and gap doesn’t create spacing. Switch to flex or grid and the same gap suddenly becomes meaningful.

Let’s do something closer to real life: a responsive card gallery with images.

.gallery {
  gap: 20px;
}


.gallery {
gap: 8px;
} 


.gallery {
gap: 36px;
} 


*,
::before,
::after {
box-sizing: border-box;
}

.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
padding: 14px;
border: 3px solid #111;
border-radius: 14px;
background: #f6f6f6;
max-width: 860px;
font-family: ui-sans-serif, system-ui, sans-serif;
}

.card {
border: 2px solid #111;
border-radius: 14px;
overflow: hidden;
background: #fff;
}

.card img {
display: block;
width: 100%;
height: 120px;
object-fit: cover;
}

.card .body {
padding: 12px;
}

.card .title {
font-weight: 700;
margin: 0 0 6px 0;
}

.card p {
margin: 0;
opacity: 0.85;
} 



Click each snippet to activate a different spacing scale. This is a great workflow in real projects: pick a spacing token (8, 12, 16, 20, 24, 32...) and apply it consistently.

Responsive gap with clamp() and CSS variables

You don’t have to keep gap fixed. A common modern pattern is to make spacing responsive:

  • Use clamp(min, preferred, max) so gap grows with the viewport but stays within limits.
  • Store it in a CSS variable so multiple components share the same spacing.
.layout {
  --space: clamp(10px, 2vw, 28px);
  gap: var(--space);
}


.layout {
--space: clamp(4px, 1.2vw, 14px);
gap: var(--space);
} 


.layout {
--space: clamp(16px, 3vw, 44px);
gap: var(--space);
} 


*,
::before,
::after {
box-sizing: border-box;
}

.layout {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 14px;
border: 3px solid #111;
border-radius: 14px;
background: #f6f6f6;
max-width: 820px;
font-family: ui-sans-serif, system-ui, sans-serif;
}

.panel {
border: 2px solid #111;
border-radius: 14px;
background: #fff;
padding: 14px;
}

.panel strong {
display: block;
margin-bottom: 6px;
} 


Left

Resize the preview area if your playground supports it.

Right

Gap grows within the clamp limits.

Bottom left

Same variable, consistent spacing.

Bottom right

Try different clamp recipes.

This approach helps spacing feel “designed” across different screen sizes, without writing a bunch of media queries.

Learn more about clamp() in the CSS Clamp Interactive Tutorial.

Animating gap (yes, you can)

Because gap is a length value, it can be animated with transitions. This can be a tasteful micro-interaction, like expanding spacing on hover.

.row {
  gap: 10px;
  transition: gap 200ms ease;
}

.row:hover {
gap: 26px;
} 


*,
::before,
::after {
box-sizing: border-box;
}

.row {
display: flex;
padding: 14px;
border: 3px solid #111;
border-radius: 14px;
background: #f6f6f6;
max-width: 760px;
font-family: ui-sans-serif, system-ui, sans-serif;
}

.block {
border: 2px solid #111;
border-radius: 12px;
background: #fff;
padding: 14px 18px;
} 


Hover
to
change
gap

Tip: if you use lots of motion effects, consider respecting reduced motion preferences with a media query.

“Gap not working” troubleshooting checklist

  1. Is the container actually a layout container? You need display: grid or display: flex (or multi-column for column-gap).
  2. Is the gap on the parent? Gap belongs on the container, not the children.
  3. Are you expecting space around the outside? Gap only creates space between items. Use padding on the container for outer spacing.
  4. Are you mixing margin and gap? You can, but margins may add extra spacing you didn’t expect. Temporarily remove margins to see what gap is doing.
  5. Flex wrapping confusion? If you want multiple lines, you need flex-wrap: wrap. Gap won’t magically create new rows; it just spaces whatever layout you already have.

Gap quick reference

  • gap: 20px; sets both row and column gaps to 20px.
  • gap: 12px 24px; sets row gap to 12px and column gap to 24px.
  • row-gap: 16px; controls vertical spacing in grid/flex.
  • column-gap: 16px; controls horizontal spacing in grid/flex (and between columns in multi-column layout).
  • Gap is best paired with container padding to create a clean “outside + inside” spacing system.

Best practices for using gap

  • Use consistent spacing steps (like 8, 12, 16, 20, 24, 32).
  • Prefer gap over margins for layout spacing inside grids and flex rows.
  • Keep “outer spacing” separate: use container padding for the outside and gap for the inside.
  • If you find yourself adding margins to every child, consider moving that spacing responsibility to the container with gap.

That’s the core of the gap property: simple, predictable, and a huge quality-of-life upgrade for layouts. Happy spacing!