Grid is CSS's native two-dimensional layout system — rows and columns controlled together, with named lines and areas that make complex page layouts readable instead of a nested-div maze.
cssgridlayoutresponsive
Knowledge Check
4 questions · pass at 70%
0/4
easymcq
1. What does the `fr` unit represent in CSS Grid?
Interview Questions
3 questions
Cheatsheet
Download-ready reference
Cheatsheet
display: grid; → TWO-dimensional layout — rows AND columns together
Defining tracks:
grid-template-columns: 200px 1fr 2fr; fixed + proportional (fr = share of remaining space)
grid-template-rows: auto 1fr auto;
gap: 1rem; spacing between all tracks
Responsive grid, zero media queries:
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
auto-fill → keeps empty tracks (gaps if few items)
auto-fit → collapses empty tracks (items stretch to fill)
Placing items (lines are BOUNDARIES, 1-indexed, not the tracks themselves):
grid-column: 1 / 3; from line 1 to line 3 = spans 2 tracks
grid-column: 1 / span 2; equivalent, clearer intent
Named areas (most readable for complex layouts):
grid-template-areas:
"sidebar header"
"sidebar main"
"sidebar footer";
.sidebar { grid-area: sidebar; }
Implicit grid (auto-generated tracks beyond explicit definition):
grid-auto-rows: 150px;
grid-auto-flow: row | column | dense;
Subgrid (nested grid inherits parent's actual tracks):
.child { grid-template-columns: subgrid; }
→ solves "align internal elements across sibling cards" problem
Decision: Grid = 2D (page layout, galleries) | Flexbox = 1D (nav bars, button rows)
→ commonly combined: Grid for skeleton, Flexbox inside individual cells
CSS Grid is a two-dimensional layout system — unlike flexbox (one axis at a time), Grid controls rows and columns simultaneously, with items placeable anywhere in that grid by line number, line name, or named area. This makes it the right tool for actual page-level or component-level layouts (a dashboard, a photo gallery, a page with header/sidebar/main/footer) where flexbox would require nested wrapper divs to fake two dimensions.
.container { display: grid; grid-template-columns: 200px 1fr 1fr; /* 3 columns: fixed, then two flexible */ grid-template-rows: auto 1fr auto; /* 3 rows: content-sized, flexible, content-sized */ gap: 1rem;}
fr (fraction unit) is Grid's most important unit — it represents a share of the remaining space after fixed-size tracks are accounted for. 1fr 1fr splits remaining space equally; 1fr 2fr gives the second column twice the first's share.
repeat() and minmax() — the responsive grid workhorse#
This single line creates a fully responsive gallery with zero media queries: as many columns as fit, each at least 200px, growing to fill leftover space equally. auto-fill creates as many tracks as fit (leaving empty tracks if there's extra space); auto-fit collapses empty tracks so existing items stretch to fill the row instead — the difference only matters when there are fewer items than would fill a row.
.item { grid-column: 1 / 3; /* start at line 1, end at line 3 (spans 2 columns) */ grid-row: 2 / 4; /* shorthand: grid-column: 1 / span 2; is equivalent to 1 / 3 */}
Grid lines are numbered starting at 1 (not the tracks/cells — the lines between and around them). 1 / 3 means "from line 1 to line 3," which spans across the first two column tracks.
Named grid areas — the most readable Grid pattern#
grid-template-areas lets you describe a layout as an actual ASCII picture of the page — genuinely more readable than reasoning about numbered lines for anything beyond a simple grid, and it's self-documenting in the CSS file itself.
.container { display: grid; grid-template-columns: repeat(3, 1fr); /* explicit: exactly 3 columns defined */ grid-auto-rows: 150px; /* implicit: any rows created beyond what's explicitly defined get this size */ grid-auto-flow: row | column | dense; /* how auto-placed items fill the grid */}
If you have more items than defined grid cells, Grid creates additional implicit tracks automatically. grid-auto-rows/grid-auto-columns control the size of those auto-generated tracks; grid-auto-flow: dense backfills gaps left by differently-sized items (useful for masonry-like layouts) at the cost of potentially reordering visual placement away from source order.
.parent { display: grid; grid-template-columns: repeat(4, 1fr);}.child { display: grid; grid-column: 1 / 4; grid-template-columns: subgrid; /* inherits the parent's actual column tracks, not new independent ones */}
Before subgrid (now broadly supported), a nested grid had no way to align its own tracks with its parent's tracks — two independent grids next to each other could never have their columns line up unless you duplicated the exact same track definitions manually. subgrid solves the classic "cards in a row need their internal elements to align across cards, even though each card is layout-independent" problem properly, covered further in the Modern CSS topic.
Component-internal layout, distributing items in a line
Page/section-level layout, precise 2D placement
Content-driven sizing
Very natural
Possible but less the default mode
Overlapping items
Awkward
Natural (items can share grid cells)
A useful heuristic: "do I care about alignment across both rows and columns at once?" — if yes, Grid. If you're just distributing items along one line and wrapping, Flexbox is usually simpler and sufficient. They're frequently combined: Grid for the page skeleton, Flexbox inside individual grid cells/components.
1. Reaching for nested flexbox to fake two-dimensional layout#
/* Awkward: flexbox rows of flexbox columns, fighting to align across rows */.row { display: flex; }.row > * { flex: 1; }
If columns need to align consistently across multiple rows (a real grid, not just wrapped items), this is exactly what Grid is for — nested flexbox can approximate it but fights you on alignment the moment content sizes differ between rows.
/* Wrong: intending to span 2 columns but only spans 1 */.item { grid-column: 1 / 2; } /* this is ONE column track, not two *//* Right */.item { grid-column: 1 / 3; } /* or: grid-column: 1 / span 2; */
Grid lines are the boundaries, not the tracks — a common source of off-by-one confusion, especially coming from zero-indexed thinking. span syntax (1 / span 2) avoids having to calculate the end line manually and is usually clearer intent.
3. Forgetting minmax() in repeat(auto-fit/auto-fill, ...), causing items to overflow or never wrap#
/* Wrong: 1fr alone has no minimum, items can shrink to nothing before wrapping ever happens */grid-template-columns: repeat(auto-fill, 1fr);/* Right */grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
Without a minmax() minimum, repeat(auto-fill, 1fr) just creates a single column that never wraps, since 1fr has no defined minimum width for the browser to decide "this doesn't fit, wrap to a new row."
4. Confusing auto-fill and auto-fit and being surprised by extra whitespace or stretched items#
auto-fill keeps empty tracks (visible as gaps if there are fewer items than would fill a row); auto-fit collapses them, letting existing items stretch into the freed space. Test with a small number of items specifically — this is where the difference actually shows up, they're identical when there are enough items to fill every row.
5. Using grid-template-areas with area names that don't form a valid rectangle#
/* Invalid — "header" doesn't form a rectangle */grid-template-areas: "header header" "sidebar header";
Every named area must form a complete rectangle across the grid — the browser will reject/ignore an invalid grid-template-areas declaration if the shapes don't tile into rectangles correctly.
Grid for two-dimensional/page-level layout, Flexbox for one-dimensional/component-internal layout — and combine them freely, they're not mutually exclusive.
repeat(auto-fill, minmax(Npx, 1fr)) as the default pattern for responsive card/gallery grids without media queries.
grid-template-areas for any layout complex enough that numbered-line placement gets hard to read — the ASCII-art readability is worth it.
Use span syntax (1 / span 2) over manually calculated end lines to avoid off-by-one errors.
Reach for subgrid when nested components need their internal tracks to align with an ancestor grid's tracks, rather than duplicating track definitions.
(, ) for complex layouts referenced from multiple places, improving readability over bare numbers.