Skip to content

Fix: CSS Grid Layout Not Working — Items Not Placing or Spanning Correctly

FixDevs ·

Quick Answer

How to fix CSS Grid issues — implicit vs explicit grid, grid-template-areas, auto-placement, subgrid, alignment, and common mistakes with grid-column and grid-row.

The Problem

CSS Grid items don’t position where expected:

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

.item {
  grid-column: 2 / 4; /* Supposed to span columns 2 and 3 */
}
/* Item doesn't span correctly — or disappears entirely */

Or a named area layout doesn’t render:

.container {
  display: grid;
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
}

.header { grid-area: header; } /* Has no effect */

Or items overflow the grid or stack instead of spreading across columns.

Why This Happens

CSS Grid has a large surface area and several non-obvious behaviors:

  • display: grid on the parent, not the children — a common mistake is applying display: grid to the items rather than the container.
  • Implicit vs explicit grid — columns/rows you define are “explicit.” Items that don’t fit create “implicit” tracks with auto sizing, which may not match your intent.
  • Line numbering starts at 1grid-column: 1 / 3 spans from line 1 to line 3 (covering 2 columns). Negative numbers count from the end: -1 is the last line.
  • grid-template-areas requires every cell — each row in the template must have the same number of cells, and every area name must form a rectangle. A . represents an empty cell.
  • grid-area name must match exactlygrid-area: header requires "header" in grid-template-areas. Case-sensitive.
  • gap vs grid-gapgrid-gap is deprecated. Use gap. Some older browsers need the prefix.

Fix 1: Verify display: grid Is on the Container

display: grid turns an element into a grid container. Its direct children become grid items:

/* WRONG — grid on the item, not the container */
.item {
  display: grid;
  grid-column: 1 / 3;
}

/* CORRECT — grid on the container */
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

.item {
  grid-column: 1 / 3; /* Now this works */
}

Grid only affects direct children:

<div class="container">      <!-- display: grid here -->
  <div class="item">         <!-- Direct child = grid item ✓ -->
    <span>Not a grid item</span>  <!-- Grandchild = NOT a grid item -->
  </div>
</div>

Fix 2: Understand Grid Line Numbers

Grid lines are numbered starting at 1. A 3-column grid has 4 vertical lines:

  1     2     3     4
  |  A  |  B  |  C  |

grid-column: 1 / 2  → column A only
grid-column: 1 / 3  → columns A and B
grid-column: 2 / 4  → columns B and C
grid-column: 1 / -1 → all columns (1 to last line)
grid-column: span 2 → span 2 columns from current position
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

/* Span from column 2 to end */
.item {
  grid-column: 2 / -1;   /* Line 2 to last line */
}

/* Span 2 columns starting from wherever auto-placed */
.wide-item {
  grid-column: span 2;
}

/* Place at specific position */
.positioned {
  grid-column: 2;        /* Shorthand for 2 / 3 */
  grid-row: 1 / 3;       /* Span 2 rows */
}

Fix 3: Fix grid-template-areas

Named areas must form complete rectangles, and every row needs the same cell count:

/* WRONG — uneven row lengths */
.container {
  display: grid;
  grid-template-areas:
    "header header header"
    "sidebar main"      /* Only 2 cells — error */
    "footer";           /* Only 1 cell — error */
}

/* WRONG — area doesn't form a rectangle */
.container {
  display: grid;
  grid-template-areas:
    "header sidebar"
    "main   sidebar"
    "footer header"; /* 'header' appears in two non-contiguous rows — invalid */
}

/* CORRECT — rectangular areas, equal row lengths */
.container {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header"
    "sidebar main  "
    "footer  footer";
  min-height: 100vh;
}

.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.footer  { grid-area: footer; }

Use . for empty cells:

.container {
  grid-template-areas:
    "logo    nav    nav"
    ".       content content"
    "footer  footer  footer";
}
/* The dot '.' leaves that cell empty */

Fix 4: Fix Implicit Grid Behavior

When items don’t fit in the explicit grid, they’re placed in the implicit grid using auto-sizing:

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  /* Only defines columns — rows are implicit */
}

/* Implicit rows default to auto (content height) */
/* To control implicit row size: */
.container {
  grid-auto-rows: 200px;          /* Fixed height */
  grid-auto-rows: minmax(100px, auto); /* Min 100px, grows with content */
}

/* Auto-placement direction */
.container {
  grid-auto-flow: row;    /* Default: fill rows left to right */
  grid-auto-flow: column; /* Fill columns top to bottom */
  grid-auto-flow: row dense; /* Fill gaps when items span multiple cells */
}

dense packing to fill gaps:

/* Without dense: a 2-column-wide item leaves gaps before smaller items */
/* With dense: smaller items backfill gaps */
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-flow: row dense;
}

.wide { grid-column: span 2; }
.tall { grid-row: span 2; }

Fix 5: Alignment and Stretching

Grid items stretch to fill their cell by default. Override with alignment properties:

/* Container-level alignment (affects ALL items) */
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);

  /* Align items along the row axis (horizontal) */
  justify-items: stretch; /* default */
  justify-items: start;
  justify-items: end;
  justify-items: center;

  /* Align items along the column axis (vertical) */
  align-items: stretch;   /* default */
  align-items: start;
  align-items: end;
  align-items: center;

  /* Shorthand */
  place-items: center;    /* align-items + justify-items */
  place-items: start end; /* vertical horizontal */
}

/* Align the grid tracks within the container */
.container {
  width: 800px;
  grid-template-columns: repeat(3, 200px); /* Total: 600px < 800px */

  justify-content: start;    /* default — tracks at start */
  justify-content: center;   /* tracks centered */
  justify-content: space-between;
  justify-content: space-around;
}

/* Per-item alignment override */
.item {
  justify-self: center;  /* Override justify-items for this item */
  align-self: end;       /* Override align-items for this item */
}

Fix 6: Use minmax() and fr Units Correctly

fr units and minmax() enable flexible layouts without overflow:

/* fr unit — fraction of remaining space */
.container {
  display: grid;

  /* 3 equal columns */
  grid-template-columns: repeat(3, 1fr);

  /* 1fr min 200px — never smaller than 200px */
  grid-template-columns: repeat(3, minmax(200px, 1fr));

  /* Auto-fill: as many columns as fit, min 200px each */
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));

  /* Auto-fit: same as auto-fill but collapses empty tracks */
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

auto-fill vs auto-fit:

/* auto-fill — creates empty columns to fill the row */
/* auto-fit — collapses empty columns, stretches existing items */

/* For responsive card grids, auto-fit is usually better: */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 1.5rem;
}
/* Cards fill the available width at any viewport size */

Avoid fr with content overflow:

/* PROBLEM: fr doesn't prevent overflow from long content */
.container {
  grid-template-columns: 1fr 1fr;
}
.item {
  /* Long word or code block can overflow a 1fr column */
}

/* FIX: use minmax with min-content */
.container {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  /* minmax(0, 1fr) allows column to shrink below content size */
}

Fix 7: Subgrid for Nested Alignment

Subgrid lets nested grids align to the parent grid’s tracks:

/* Parent grid */
.layout {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

/* Card that spans 2 columns and uses parent's column tracks */
.card {
  grid-column: span 2;
  display: grid;
  grid-template-columns: subgrid; /* Uses parent's 2 remaining column tracks */
}

/* Card content aligns to parent grid lines */
.card-image {
  grid-column: 1;    /* First track of parent grid under the card */
}
.card-text {
  grid-column: 2;    /* Second track */
}

Note: subgrid is supported in all modern browsers as of 2023. Check if your target browsers support it before using.

Still Not Working?

height: 100% on grid items — grid items don’t automatically fill the container’s height unless align-items: stretch (the default) is set on the container. If items have explicit heights set, they won’t stretch.

Overflow from non-grid children — if a non-grid element (e.g., an absolutely positioned child) causes overflow, it won’t affect grid layout but may visually overflow the container. Use overflow: hidden on the container or position: relative on a wrapper.

CSS Grid in Firefox vs Chrome — minor rendering differences exist between browsers. Firefox DevTools has the best CSS Grid inspector — use it to visualize grid lines, areas, and gaps.

grid shorthand is complex — the grid shorthand property combines grid-template-rows, grid-template-columns, grid-template-areas, grid-auto-rows, grid-auto-columns, and grid-auto-flow. Avoid the shorthand until you’re comfortable with each individual property.

For related CSS issues, see Fix: CSS Flexbox Not Working and Fix: CSS Animation Not Working.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles