CSS Grid was finalized in spring 2017 and was available within a month on most major browsers. Where Flexbox handles one dimension at a time — either a row or a column — Grid handles both simultaneously, giving you direct control over the horizontal and vertical structure of a layout in a single system.
Use Flexbox when you have a set of items and want the browser to figure out how to distribute them. Use Grid when you already know the structure and want to place things precisely within it. In practice, you will use both together: Grid for the page-level skeleton, Flexbox for the components that live inside it.
For quick reference use the CSS Grid Cheat Sheet. For a deeper introduction, Mozilla’s Grid article is the best starting point. Rachel Andrew’s Grid by Example collects ready-to-use patterns on CodePen. CSS Tricks has a complete reference guide. When debugging, open Firefox’s inspector, click on any grid element, and activate the grid overlay — it draws the lines directly on the page.
Grid Terminology
Grid lines are the invisible horizontal and vertical dividers that define the grid. They are numbered starting at 1 from the top-left. Negative numbers count inward from the far edge: -1 is always the last line.
Grid tracks are the rows and columns between two adjacent grid lines.
Grid cells are the individual intersections of a row track and a column track — the smallest unit of the grid.
Grid areas are one or more adjacent cells forming a rectangle. An area can span multiple rows and columns.
Grid gaps are the gutters between tracks. gap: 50px 100px; sets a 50px row gap and a 100px column gap. If both values are equal, use a single value: gap: 20px;.
Grid Container (parent)
CSS Grid controls are set on the parent element, which becomes the grid container. All direct children of the container become grid items and are placed on the grid automatically unless you specify otherwise.
DISPLAY
Initiate the parent as a grid container with display: grid; for a block-level container, or display: inline-grid; for an inline-level container. As with Flexbox, only direct children are affected.
GRID-TEMPLATE-COLUMNS and GRID-TEMPLATE-ROWS
These two properties define the track structure of the grid. The values you provide are a track list — a space-separated sequence of sizes, one per track. The spaces between the values implicitly define the grid lines.
.grid-container {
display: grid;
grid-template-columns: 200px 1fr 2fr;
grid-template-rows: auto 100px;
gap: 20px;
}
Track sizes can be fixed lengths, percentages, or fractions of available space using the fr unit. 1fr means one share of the remaining space. grid-template-columns: 1fr 2fr 1fr; gives you three columns where the middle one is twice the width of the outer two.
THE FR UNIT
The fr unit is Grid’s native way of distributing space proportionally. It operates on the remaining space after fixed and percentage values are resolved. grid-template-columns: 200px 1fr; gives the first column a fixed 200px and the second column everything that remains.
REPEAT()
When a track pattern repeats, use repeat() to avoid writing it out by hand. The first argument is the repeat count, the second is the track definition to repeat.
/* Fixed repeat: four equal columns */
grid-template-columns: repeat(4, 1fr);
/* Auto-fill: as many 150px columns as fit */
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
/* Auto-fit: same as auto-fill but collapses empty tracks */
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
auto-fill and auto-fit are the key to responsive grids without media queries. The grid creates as many columns as fit at the minimum size, and grows them to fill available space. The difference shows when there are fewer items than columns: auto-fill keeps the empty tracks, auto-fit collapses them so the existing items can expand.
NAMED LINES
Grid lines can be named by inserting square brackets between track values. Names make placement code more readable and less dependent on counting line numbers.
grid-template-columns: [sidebar-start] 200px [sidebar-end content-start] 1fr [content-end];
A line can have more than one name, separated by spaces inside the brackets.
GRID-TEMPLATE-AREAS
Grid areas let you assign names to regions of the grid and then place items by name rather than by line numbers. This makes the layout intent immediately legible from the CSS.
.grid-container {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
grid-template-areas:
“header header header”
“sidebar content aside”
“footer footer footer”;
}
header { grid-area: header; }
.sidebar { grid-area: sidebar; }
main { grid-area: content; }
aside { grid-area: aside; }
footer { grid-area: footer; }
Each string in grid-template-areas represents a row. Each word within the string represents a cell. An empty cell is denoted by a period .. Every named area must form a rectangle — L-shapes are not valid.
JUSTIFY-CONTENT and ALIGN-CONTENT
These properties control how the entire grid is positioned within the container when the grid is smaller than its container. justify-content operates on the inline (horizontal) axis, align-content on the block (vertical) axis. Values are the same as Flexbox: start, center, end, space-between, space-around, space-evenly.
JUSTIFY-ITEMS and ALIGN-ITEMS
These set the default alignment of all grid items within their cells. justify-items aligns items horizontally within the cell, align-items aligns them vertically. The default for both is stretch, which is why grid items fill their cells unless you specify otherwise. Other values: start, center, end.
#areas {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(3, 1fr);
grid-template-areas:
“header header header header”
“sidebar content content aside”
“footer footer footer footer”;
gap: 10px 20px;
justify-items: stretch;
align-items: stretch;
}
LIVE DEMO
header / sidebar / content / aside / footer
Intrinsic and Extrinsic Sizing
Grid tracks can be sized either by the container (extrinsic) or by their content (intrinsic).
Extrinsic sizing uses values like fixed lengths, percentages, and fr. The container defines the track size and the content must conform. grid-template-columns: 200px 1fr 2fr; is fully extrinsic.
Intrinsic sizing uses auto, min-content, max-content, or fit-content(). The content defines its preferred size and the grid adapts. grid-template-columns: max-content auto; gives the first column exactly the width of its widest item.
The power of Grid is that you can combine both in a single definition. grid-template-columns: max-content 1fr minmax(150px, 25%); gives you a content-fitted first column, a flexible middle, and a constrained third. minmax() takes a minimum and maximum and lets the track size negotiate between them.
Subgrid
A grid nested inside another grid has no connection to the parent grid’s line structure. Columns and rows in the child are independent and cannot be made to align with the parent. Subgrid solves this.
Setting grid-template-columns: subgrid; on a child element tells it to inherit the parent’s column tracks across whatever span the child occupies. The child does not define new tracks — it participates in the parent’s.
.parent {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
gap: 1rem;
}
.child {
grid-column: 1 / -1; /* span all columns */
display: grid;
grid-template-columns: subgrid; /* inherit parent’s columns */
}
Without subgrid, card components in a grid will have internal elements — headings, body text, footers — that cannot be made to align across cards without either JavaScript or fragile fixed values. Subgrid makes that alignment automatic and responsive.
Grid Item (children)
Grid items are the direct children of the grid container. By default they flow into the grid sequentially, filling cells left to right and top to bottom. Any or all of them can be placed and sized explicitly using item-level properties.
GRID-COLUMN and GRID-ROW
Place an item by specifying which grid lines it should start and end on. grid-column: 2 / 4; spans from column line 2 to column line 4, occupying two column tracks. Use span as shorthand: grid-column: 2 / span 2; means the same thing. Use named lines if you defined them: grid-column: sidebar-start / content-end;.
GRID-AREA
grid-area is shorthand for all four placement values at once: grid-row-start / grid-column-start / grid-row-end / grid-column-end. When using grid-template-areas, grid-area is also how you assign an item to a named area by name.
ORDER
As in Flexbox, order changes the visual position of an item relative to its auto-flow position without changing the HTML. Default is 0. Negative values move an item earlier, positive values move it later. Use with caution — it disconnects visual order from DOM order, which breaks keyboard navigation and screen reader sequence.
JUSTIFY-SELF and ALIGN-SELF
Override the container’s justify-items and align-items for a single item. Useful for exceptions: a single item that should be centered while the rest stretch. Values: start, center, end, stretch.
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
align-items: stretch;
}
.grid-container div:nth-child(1) {
grid-column: span 2;
}
.grid-container div:nth-child(3) {
justify-self: center;
align-self: end;
}
.grid-container div:nth-child(4) {
grid-row: span 2;
}
LIVE DEMO
Box spanning two columns / single box / centered and bottom-aligned box / box spanning two rows
Page Layout Demonstration
The Holy Grail layout — header, three-column body with sidebar and aside, footer — was the white whale of CSS layout for a decade. It required floats, negative margins, and a careful ordering of columns in the HTML to make the main content column appear first for screen readers. With Grid it is a dozen lines.
.holy-grail {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
grid-template-areas:
“header header header”
“sidebar content aside”
“footer footer footer”;
min-height: 100vh;
gap: 20px;
}
header { grid-area: header; }
nav { grid-area: sidebar; }
main { grid-area: content; }
aside { grid-area: aside; }
footer { grid-area: footer; }
@media (max-width: 600px) {
.holy-grail {
grid-template-columns: 1fr;
grid-template-areas:
“header”
“content”
“sidebar”
“aside”
“footer”;
}
}
The HTML order can now match reading order — header, main content, navigation, aside, footer — independent of the visual layout. The responsive version requires only a redefined grid-template-areas in the media query. No floats, no clearing, no negative margins.