CSS Grid has a rich alignment system with six properties that control how the entire grid sits within its container and how individual items sit within their cells. While Flexbox alignment operates along a single axis, Grid alignment works independently on both the row and column axes simultaneously. In this lesson you will master all six alignment properties and understand the key differences between aligning the grid tracks versus aligning individual items.
The Six Grid Alignment Properties
| Property | Applies To | Controls |
|---|---|---|
justify-content |
Container | Positions the entire grid along the inline (row) axis — when grid is smaller than container |
align-content |
Container | Positions the entire grid along the block (column) axis — when grid is smaller than container |
justify-items |
Container | Default inline alignment of all items within their cells |
align-items |
Container | Default block alignment of all items within their cells |
justify-self |
Item | Override inline alignment for one specific item |
align-self |
Item | Override block alignment for one specific item |
Common Values for All Six Properties
| Value | Meaning | Items/Tracks Behaviour |
|---|---|---|
start |
Align to start edge of the axis | Items: left/top of cell. Tracks: left/top of container |
end |
Align to end edge of the axis | Items: right/bottom of cell. Tracks: right/bottom of container |
center |
Centre along the axis | Items centred in cell. Tracks grouped in centre |
stretch |
Default for items — fills the cell | Items fill entire cell width/height |
space-between |
Tracks/content only — equal gaps between | First and last tracks at container edges |
space-evenly |
Tracks/content only — equal gaps everywhere | Equal space including outer edges |
place-items and place-content Shorthand
| Shorthand | Expands To | Example |
|---|---|---|
place-items: A B |
align-items: A; justify-items: B |
place-items: center start |
place-items: A |
align-items: A; justify-items: A |
place-items: center — both axes |
place-content: A B |
align-content: A; justify-content: B |
place-content: center space-between |
place-self: A B |
align-self: A; justify-self: B |
place-self: end center |
justify-items / align-items control how items are aligned inside their cells. justify-content / align-content control how the entire grid of tracks is positioned inside the container — these only have a visible effect when the sum of tracks is smaller than the container. With 1fr tracks, content always fills the container, so justify-content does nothing.place-self: center on the item, or place-items: center on the container to centre all items. This is cleaner than the classic display: flex; align-items: center; justify-content: center wrapper trick for grid scenarios.justify-items or align-items to anything other than stretch means items no longer fill their cell. An item that was 100% of its cell width with stretch will shrink to fit-content size with center or start. This is often surprising — use justify-self or align-self on individual items when you only want to change alignment for specific cells.Basic Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Grid Alignment</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; padding: 32px; background: #f8fafc; }
.label { font-size: 0.75rem; font-weight: 700; text-transform: uppercase;
letter-spacing: 0.06em; color: #64748b; margin-bottom: 8px; }
.grid-wrap {
background: #e0e7ff;
border-radius: 10px;
padding: 8px;
margin-bottom: 24px;
height: 200px; /* taller than tracks to show content alignment */
}
.demo-grid {
display: grid;
grid-template-columns: repeat(3, 120px); /* fixed — leaves horizontal space */
grid-auto-rows: 80px;
gap: 8px;
height: 100%;
}
.cell {
background: #4f46e5;
border-radius: 6px;
color: white;
font-size: 0.75rem;
font-weight: 700;
display: flex; align-items: center; justify-content: center;
}
/* justify-content: tracks inside container (horizontal) */
.jc-center { justify-content: center; }
.jc-end { justify-content: end; }
.jc-sb { justify-content: space-between; }
/* align-content: tracks inside container (vertical) */
.ac-center { align-content: center; }
.ac-end { align-content: end; }
/* place-content shorthand */
.pc-center { place-content: center; }
/* justify-items: items inside their cells */
.ji-center { justify-items: center; }
.ji-start { justify-items: start; }
/* align-items: items inside cells vertically */
.ai-end { align-items: end; }
/* Individual self overrides */
.self-demo { place-content: center; place-items: start; }
.self-center { place-self: center; background: #10b981; }
.self-end { align-self: end; justify-self: end; background: #f59e0b; }
</style>
</head>
<body>
<div class="label">justify-content: center — tracks centred horizontally</div>
<div class="grid-wrap">
<div class="demo-grid jc-center ac-center">
<div class="cell">A</div><div class="cell">B</div><div class="cell">C</div>
</div>
</div>
<div class="label">place-content: center — entire grid centred both axes</div>
<div class="grid-wrap">
<div class="demo-grid pc-center">
<div class="cell">A</div><div class="cell">B</div><div class="cell">C</div>
</div>
</div>
<div class="label">justify-items: center — items centred inside their cells</div>
<div class="grid-wrap">
<div class="demo-grid jc-center ac-center ji-center ai-end" style="grid-template-columns:repeat(3,1fr)">
<div class="cell" style="width:80px">A</div>
<div class="cell" style="width:80px">B</div>
<div class="cell" style="width:80px">C</div>
</div>
</div>
<div class="label">Individual place-self overrides — green: center, yellow: end-end</div>
<div class="grid-wrap">
<div class="demo-grid self-demo" style="grid-template-columns:repeat(3,1fr);place-items:start">
<div class="cell" style="width:70px">start</div>
<div class="cell self-center" style="width:70px">center</div>
<div class="cell self-end" style="width:70px">end</div>
</div>
</div>
</body>
</html>
How It Works
Step 1 — Fixed Tracks Leave Space for justify-content
Three 120px fixed columns total 360px. In an 800px container, 440px of horizontal space is left over. justify-content: center distributes this surplus equally on both sides of the track group, centering the entire grid horizontally within the container.
Step 2 — place-content: center Centres Both Axes
place-content: center is shorthand for align-content: center; justify-content: center. With the container taller than the track heights (container: 184px inner, tracks: 2×80px + gap = 168px), the vertical surplus is also centred. All four sides have equal space.
Step 3 — justify-items Shrinks Items to Fit-Content
With fluid 1fr columns, each cell is ~260px wide. Setting justify-items: center makes the 80px-wide items center within their 260px cells — they no longer stretch to fill the full cell width. The background of each item covers only 80px, not the full cell.
Step 4 — place-self Overrides Container Alignment
With the container set to place-items: start (all items top-left), the green item uses place-self: center to centre itself within its cell, and the yellow item uses align-self: end; justify-self: end to pin itself to the bottom-right of its cell.
Step 5 — stretch Is the Productive Default
The default align-items: stretch; justify-items: stretch makes all items fill their grid cells completely — row-height and column-width determined by the track definitions. This is why grid-based card layouts look clean without explicitly sizing every card.
Real-World Example: Product Grid with Centred Thumbnails
/* product-alignment.css */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; background: #f8fafc; padding: 40px 24px; }
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 20px;
/* align-items: stretch by default — all cards same height */
}
.product-card {
background: white;
border-radius: 16px;
border: 1px solid #e2e8f0;
overflow: hidden;
display: grid;
grid-template-rows: 200px auto 1fr auto; /* img, tag, text, btn */
}
/* Image cell: item centred within 200px row */
.product-img-cell {
background: #f1f5f9;
display: grid;
place-items: center; /* centre the img within the cell */
overflow: hidden;
}
.product-img-cell img {
width: 140px;
height: 140px;
object-fit: contain;
transition: transform 0.3s;
}
.product-card:hover .product-img-cell img { transform: scale(1.05); }
.product-tag {
padding: 12px 16px 0;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #4f46e5;
}
.product-info { padding: 8px 16px 0; }
.product-name { font-size: 0.95rem; font-weight: 700; color: #0f172a; margin-bottom: 4px; }
.product-desc { font-size: 0.8rem; color: #64748b; line-height: 1.5; }
.product-footer {
padding: 16px;
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1px solid #f1f5f9;
margin-top: 12px;
align-self: end; /* pin footer to bottom of card regardless of content */
}
.product-price { font-size: 1.1rem; font-weight: 800; color: #0f172a; }
.add-btn {
background: #4f46e5; color: white;
border: none; border-radius: 8px;
padding: 8px 14px; font-size: 0.8rem; font-weight: 600;
cursor: pointer;
}
Common Mistakes
Mistake 1 — Expecting justify-content to work with fr tracks
❌ Wrong — fr tracks always fill all available space; no surplus for justify-content:
.grid { display: grid; grid-template-columns: repeat(3, 1fr); justify-content: center; }
/* 1fr tracks fill 100% of container — justify-content has no free space to act on */
✅ Correct — use fixed track sizes to leave space for justify-content:
.grid { display: grid; grid-template-columns: repeat(3, 200px); justify-content: center; }
Mistake 2 — Confusing justify-items with justify-content
❌ Wrong — using justify-content to centre items inside their cells:
.grid { display: grid; grid-template-columns: repeat(3, 1fr); justify-content: center; }
/* Does not centre items inside cells — moves the track group */
✅ Correct — use justify-items to centre items within their cells:
.grid { display: grid; grid-template-columns: repeat(3, 1fr); justify-items: center; }
Mistake 3 — Forgetting align-content requires height on the container
❌ Wrong — container has no explicit height; tracks fill it; align-content does nothing:
.grid { display: grid; align-content: center; } /* container height = sum of tracks */
✅ Correct — give the container a height larger than the total track height:
.grid { display: grid; height: 500px; align-content: center; }
Quick Reference
| Property | Axis | Aligns | Shorthand |
|---|---|---|---|
justify-content |
Inline (row) | Whole grid in container | place-content |
align-content |
Block (col) | Whole grid in container | place-content |
justify-items |
Inline (row) | All items in their cells | place-items |
align-items |
Block (col) | All items in their cells | place-items |
justify-self |
Inline (row) | One item in its cell | place-self |
align-self |
Block (col) | One item in its cell | place-self |