CSS Grid is a two-dimensional layout system that lets you build grid-based structures using columns and rows – all defined in CSS, not in HTML.
Before Grid, we relied on floats, tables, and inline-block hacks. Then Flexbox arrived and solved one-dimensional alignment, but it was never designed for full page layouts.
CSS Grid fills that gap. It separates visual layout from markup order, so you can rearrange elements across screen sizes without touching the HTML. I’ve been using it in production for years, and it still saves me hours on every project.
What is CSS Grid?
Grid is the first CSS module built specifically for two-dimensional layouts. It handles both columns and rows at the same time, something Flexbox was never designed to do.
The key concept: you describe the layout structure at the CSS level, then let elements flow into the grid. Redefine the grid inside a Media Query and the layout adapts without any markup changes.
Because each element occupies a defined area, you don’t get the overlap or overflow problems that plagued float-based designs.
Terminology – CSS Grid
Before diving in, let’s cover the core terminology. These terms sound similar at first, but understanding the difference between them is essential for everything that follows.
Don’t worry if these terms feel abstract right now. Each one will click once you start building grids in the sections that follow.
1. Grid Container
The Grid Container is the parent element that holds the grid layout. You create it by adding display: grid or display: inline-grid to an element.
In the example below, the outer bordered area is the Grid Container:
Once an element is defined as a Grid Container, you can use various properties to control its layout. Note that, just like Flexbox, CSS Grid respects the page direction. The example above is LTR. In an RTL context, it would look like this:
2. Grid Line
Grid Lines are the dividing lines that form the structure of the grid. They run both vertically and horizontally, and are numbered starting from 1.
In a 3-column, 3-row grid there are 4 vertical lines (column lines) and 4 horizontal lines (row lines). You reference these numbers when placing items with grid-column and grid-row.
The numbers along the top are column lines (left to right) and the numbers along the left are row lines (top to bottom):
3. Grid Cell
A Grid Cell is the smallest unit in the grid – a single slot at the intersection of one row and one column. Think of it like one square on a chessboard. In a 3×3 grid, you have 9 cells.
A Grid Cell is not an HTML element. It’s a conceptual space defined by the grid structure.
The highlighted cell below shows a single Grid Cell:
4. Grid Track
A Grid Track is the space between two adjacent Grid Lines. You control track sizes with grid-template-columns and grid-template-rows.
- Horizontal tracks (between row lines) are called row tracks.
- Vertical tracks (between column lines) are called column tracks.
The highlighted row below represents a Grid Track:
5. Grid Area
A Grid Area is a rectangular region that spans one or more Grid Cells. You can define areas using grid-template-areas.
The highlighted cells below form a single Grid Area:
6. Grid Item
Grid Items are the actual HTML elements inside the grid – the direct children of the Grid Container. By default, each item occupies one cell, but items can span multiple cells, tracks, or entire areas.
With these terms covered, let’s build our first grid.
Creating your first Grid
The two core components are the Grid Container (the parent) and the Grid Items (the children inside it).
Here is the markup for a container with six items:
<div class="wrapper">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
</div>To turn this into a Grid, add display: grid:
.wrapper {
display: grid;
}The result:
Right now it looks like six stacked elements because we haven’t defined any columns or rows yet. Let’s fix that.
Creating Columns and Rows in the Grid
To make the Grid two-dimensional, we define columns and rows using grid-template-columns and grid-template-rows:
.wrapper {
display: grid;
grid-template-columns: 33% 33% 33%;
grid-template-rows: 60px 60px;
}Three values for grid-template-columns means three columns. Two values for grid-template-rows means two rows.
To verify you understand the connection between values and layout, take a look at this variation:
.wrapper {
display: grid;
grid-template-columns: 20% 60% 20%;
grid-template-rows: 80px 40px;
}You’re not limited to percentages. You can use px, viewport units, calc(), or the fr unit which we’ll cover later.
Positioning Elements in the Grid
Now let’s position items precisely. Create a 3×3 grid:
.wrapper {
display: grid;
grid-template-columns: 33% 33% 33%;
grid-template-rows: 60px 60px 60px;
}We defined three rows but only see two filled rows because there are only six items. The third row exists but remains empty.
To change an item’s position or size, target it and use grid-column-start / grid-column-end:
.wrapper > div:nth-child(1) {
grid-column-start: 1;
grid-column-end: 4;
}The first item starts at Grid Line 1 and ends at Grid Line 4, spanning the entire row:
If you remember the Grid Lines example from earlier, a 3-column grid has 4 column lines and a 3-row grid has 4 row lines. Line 4 is the right edge of the last column:
The same concept applies to rows with grid-row-start / grid-row-end. Here’s a more complex example:
.wrapper > div:nth-child(1) {
grid-column-start: 1;
grid-column-end: 3;
}
.wrapper > div:nth-child(3) {
grid-row-start: 2;
grid-row-end: 4;
}
.wrapper > div:nth-child(4) {
grid-column-start: 2;
grid-column-end: 4;
}There’s a shorter syntax using grid-column and grid-row:
.wrapper > div:nth-child(1) {
grid-column: 1 / 3;
}
.wrapper > div:nth-child(3) {
grid-row: 2 / 4;
}
.wrapper > div:nth-child(4) {
grid-column: 2 / 4;
}You can also use span to define how many tracks an item should stretch across:
.wrapper > div:nth-child(1) {
grid-column: 1 / span 2;
}
.wrapper > div:nth-child(3) {
grid-row: 2 / span 2;
}
.wrapper > div:nth-child(4) {
grid-column: 2 / span 2;
}Named Grid Lines
Instead of referencing lines by number, you can name them when defining the grid:
.wrapper {
display: grid;
grid-template-columns: [sidebar-start] 200px [sidebar-end content-start] 1fr [content-end];
grid-template-rows: [header-start] 80px [header-end main-start] 1fr [main-end];
}Then position items using those names:
.sidebar {
grid-column: sidebar-start / sidebar-end;
}
.content {
grid-column: content-start / content-end;
grid-row: main-start / main-end;
}Named lines make your CSS more readable, especially in complex layouts where line numbers become hard to track.
Aligning Elements in the Grid
CSS Grid gives you six alignment properties. They’re part of the CSS Box Alignment Module and work in both Grid and Flexbox:
justify-items/align-itemsjustify-content/align-contentjustify-self/align-self
Container-Level Alignment
For the following demos, we use a grid with three 100px columns and 50x50px items, so there’s visible space around each item:
.my-container {
display: grid;
grid-template-columns: 100px 100px 100px;
width: 100%;
height: 200px;
}
.item {
width: 50px;
height: 50px;
}The items don’t fill the cells – they sit at the top-left by default (top-right in RTL).
The justify-items property
Aligns all Grid Items along the row axis (horizontal). Try the different values:
The align-items property
Aligns all Grid Items along the column axis (vertical):
The justify-content property
When the total grid is smaller than the container, justify-content positions the entire grid along the row axis:
Here’s the same property on a grid with more items so the effect is clearer:
The align-content property
Same concept but along the column axis (vertical):
Item-Level Alignment
The justify-self and align-self properties work like justify-items and align-items, but apply to individual Grid Items instead of all items at once.
The justify-self property
Aligns a single item horizontally. Here, item 3 has justify-self: end:
<div class="my-container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item" style="justify-self: end;">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
</div>The align-self property
Aligns a single item vertically. Here, item 3 has align-self: end:
<div class="my-container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item" style="align-self: end;">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
</div>Shorthand Properties
CSS provides three shorthands that combine the justify and align properties into one line:
/* place-items = align-items / justify-items */
.container {
place-items: center;
}
/* place-content = align-content / justify-content */
.container {
place-content: space-between center;
}
/* place-self = align-self / justify-self */
.item {
place-self: end center;
}When you provide a single value, it applies to both axes. Two values set the block (vertical) axis first, then the inline (horizontal) axis.
Combining Alignment Properties
You can combine multiple alignment properties to achieve precise layouts:
.my-container {
justify-content: space-evenly;
justify-items: center;
align-content: space-evenly;
align-items: center;
}Notice how item 3 overrides align-items: center with its own align-self: end.
Implicit Grids and grid-auto-flow
So far we’ve been defining the grid structure explicitly with grid-template-columns and grid-template-rows – that’s the explicit grid. But what happens when there are more items than cells?
The browser creates additional tracks automatically. This is called the implicit grid. You control the size of those auto-created tracks with grid-auto-rows and grid-auto-columns:
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 70px;
grid-auto-rows: 50px;
}Here we defined one explicit row of 70px. Any items that overflow into additional rows will get 50px height from grid-auto-rows.
The grid-auto-flow property
By default, Grid places items row by row (grid-auto-flow: row). You can change this to fill columns first, or use the dense keyword to backfill gaps left by larger items.
Try the different values:
Items 1 and 2 both span two columns. In row mode, item 2 can’t fit next to item 1 so it drops to the next row, leaving an empty cell in column 3. Switch to dense and watch item 3 backfill that gap.
With column, items flow top-to-bottom instead of left-to-right. Try column dense to see both effects combined.
With dense, visual order may differ from source order. This can affect keyboard navigation and accessibility, so use it carefully.
The grid-area Property
The grid-area property is a shorthand for grid-row-start, grid-column-start, grid-row-end, and grid-column-end:
grid-area: <row-start> / <column-start> / <row-end> / <column-end>Check out the grid-area and grid-template-areas guide for more examples.
Here’s a grid with 11 items where item 5 uses grid-area: 1 / 2 / 5 / 7 to span a large region:
.wrapper {
display: grid;
}
.wrapper > div:nth-child(5) {
grid-area: 1 / 2 / 5 / 7;
}Overlapping Grid Items
Unlike Flexbox, Grid allows items to occupy the same cells. This creates overlapping elements that you can control with z-index:
.item-a {
grid-column: 1 / 4;
grid-row: 1 / 3;
z-index: 1;
}
.item-b {
grid-column: 2 / 5;
grid-row: 2 / 4;
z-index: 2;
}This technique is useful for layered layouts – hero sections with overlapping text, card designs with badges, or creative magazine-style grids.
The gap Property
The gap property sets spacing between grid tracks. It’s a shorthand for row-gap and column-gap.
grid-gap, grid-row-gap, and grid-column-gap names still work but are deprecated. Use the unprefixed versions: gap, row-gap, column-gap.The first value is the row gap, the second is the column gap:
.wrapper {
display: grid;
gap: 30px 10px;
}There’s a separate post covering the gap property in both Grid and Flexbox.
The fr Unit (Fractional Unit)
The fr unit distributes available space proportionally. It eliminates the math problems that come with percentages.
Consider this: if you use grid-template-columns: 33% 33% 33% with gap: 10px, the total width becomes 100% + 20px, causing overflow.
With fr units, the browser handles the math for you:
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
grid-auto-rows: 80px;
}Use the repeat() function for cleaner code: grid-template-columns: repeat(6, 1fr).
You can mix fr with fixed units. Here, two 100px columns and one flexible column:
.wrapper {
display: grid;
grid-template-columns: 100px 100px 1fr;
grid-auto-rows: 80px;
}Fractional values don’t have to be whole numbers:
.wrapper {
display: grid;
grid-template-columns: 1.5fr 3fr 4.5fr;
grid-auto-rows: 80px;
}For more on fractional units, see CSS Grid fr unit explained.
Responsive Grids with auto-fill, auto-fit, and minmax()
One of the most powerful CSS Grid patterns creates fully responsive layouts without any Media Queries:
.wrapper {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 10px;
}The minmax(150px, 1fr) function sets a minimum of 150px and a maximum of 1fr per column. Combined with auto-fit, the browser creates as many columns as fit and expands them to fill remaining space.
The difference between auto-fill and auto-fit matters when there are fewer items than available space. Try switching between them:
auto-fit collapses empty tracks and stretches existing items to fill the row. auto-fill keeps the empty tracks, so items stay at their minimum width.
For a deeper look, see the posts on the minmax() function and responsive Grid layouts without Media Queries.
Creating a Website Structure Using CSS Grid
Let’s put it all together and build a full page layout using grid-template-areas:
<div class="wrapper">
<header>header</header>
<nav>nav</nav>
<section>section</section>
<aside>aside</aside>
<footer>footer</footer>
</div>.wrapper {
display: grid;
grid-template-areas:
"header header header"
"nav section aside"
"footer footer footer";
grid-template-rows: 80px 1fr 50px;
grid-template-columns: 15% 1fr 15%;
gap: 4px;
height: 360px;
}
header { grid-area: header; }
nav { grid-area: nav; }
section { grid-area: section; }
aside { grid-area: aside; }
footer { grid-area: footer; }Look at how little code that took. The grid-template shorthand can combine rows, columns, and areas into a single property if you prefer even more compact code.
The Grid Template Areas Property
Each string in grid-template-areas represents one row. You name items with grid-area and reference those names in the template. Use a period (.) for unnamed/empty cells.
"header header header"– header spans all three columns"nav section aside"– three columns with different areas"footer footer footer"– footer spans the full width
Want the nav to span the full height? Change one CSS property:
.wrapper {
grid-template-areas:
"nav header header"
"nav section aside"
"nav footer footer";
}What about Responsiveness?
The layout already stretches and shrinks with the container. For a completely different mobile layout, add a Media Query that only changes grid-template-areas:
@media (max-width: 768px) {
.wrapper {
grid-template-areas:
"header header header"
"nav nav nav"
"section section section"
"aside aside aside"
"footer footer footer";
grid-template-rows: 50px 30px 1fr 60px 30px;
}
}Restructuring the entire page layout by changing a single CSS property is one of Grid’s best features. You can also skip Media Queries entirely using auto-fit and minmax() as shown earlier in this guide.
Subgrid
Subgrid lets nested grids inherit their parent’s track sizes. It has 97% global browser support as of 2026 (Chrome 117+, Firefox 71+, Safari 16+) and is production-ready.
The problem it solves: when you have a grid of cards, each card’s internal elements (title, description, footer) can’t align across cards because each card has its own independent grid. With Subgrid, the child grid adopts the parent’s row tracks:
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid;
gap: 8px;
}Each card spans 3 parent rows and uses grid-template-rows: subgrid to align its content with the other cards:
Brief description.
This card has more description text that takes up more vertical space than the others.
This has a medium-length description.
Notice how all three card titles, descriptions, and footers align horizontally, regardless of their content length. Without Subgrid, each card’s rows would be independent and the alignment would break.
You can also use grid-template-columns: subgrid for column inheritance. This is especially useful for form layouts where labels and inputs need to align across rows.
CSS Masonry layout has been renamed to display: grid-lanes. Safari Technology Preview supports it by default, while Chrome and Firefox still require a flag. It’s not production-ready yet, but it’s getting close.
CSS Grid vs Flexbox
Flexbox is designed for one-dimensional layouts (a row or a column). Grid is for two-dimensional layouts (rows and columns together).
One-dimensional layout – use Flexbox:
Two-dimensional layout – use Grid:
In practice, you should use both. Grid handles the page structure, Flexbox handles alignment within components like navigation bars, card contents, or button groups.
If Grid performance concerns you on large layouts, take a look at CSS Grid’s impact on web performance.
Browser Support
CSS Grid has 98% global browser support. Every modern browser has fully supported it since 2017. Use it confidently in production without fallbacks.
FAQs
Common questions about CSS Grid:
fr unit stands for "fractional unit." It distributes available space proportionally between grid tracks. For example, grid-template-columns: 1fr 2fr creates two columns where the second is twice as wide as the first. Unlike percentages, fr units automatically account for gaps and fixed-size tracks.repeat(auto-fit, minmax(min, 1fr)) for your column template. The browser will create as many columns as fit within the container, each at least min wide, and stretch them to fill remaining space. For example, grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) creates a fully responsive grid that adapts to any screen width with zero Media Queries.grid-template-rows: subgrid or grid-template-columns: subgrid. This is useful when you need child elements across multiple grid items to align with each other, like card titles and footers lining up across a row of cards. It has 97% browser support as of 2026.z-index property. This is useful for layered designs like hero sections with overlapping text, badges on cards, or creative magazine-style layouts.Summary
This guide covered the core of CSS Grid: containers, items, tracks, areas, alignment, positioning, the gap and fr unit, grid-template-areas for page layouts, implicit grids, auto-flow, subgrid, and overlapping items.
Grid cuts layout code significantly compared to older techniques. To go deeper, check out the dedicated posts linked throughout this guide, or take a look at CSS Variables – another feature you’ll want in your toolkit.
If you need components to adapt their layout based on available space rather than viewport width, CSS Container Queries pair perfectly with Grid.


What a wonderful guide 🙂 Thanks!