Frontend Development · Tutorial 5 of 7
CSS Grid in Depth: template areas, auto-fit and minmax
By the FUTEAC Team · 13 November 2026 · 11 min read · Intermediate
In short
CSS Grid lays out rows and columns together. Name areas in grid-template-areas to sketch a whole page in a few lines, size tracks with fr units that share leftover space, and build galleries with repeat(auto-fit, minmax(180px, 1fr)) that add or drop columns as the container changes width, with no media query at all.
- Two directions at once. Grid controls rows and columns together. That is the whole difference from Flexbox, which works along one axis at a time.
- Template areas are a sketch of the page. Reading the quoted area names tells you the layout without tracing line numbers.
- fr units share what is left. A 180px sidebar plus a 1fr main area means the main area takes the remaining 420px of a 600px container.
- auto-fit plus minmax reflows with no media query. The browser fits as many 180px tracks as it can, then 1fr stretches them to fill the row.
- This tutorial feeds the HTML & CSS Foundations course (/course/html-css-foundations) and the CSS Grid (/skill/css-grid) skill.
Before you start
What is Grid for, in one paragraph?
Flexbox lays items out along one line. Grid lays them out in rows and columns at the same time. If Flexbox is arranging books on a shelf, Grid is a muffin tray: every slot has a row and a column, and you decide what goes in each slot. That is why the decision rule from item 19 still holds: one direction, Flexbox; two directions, Grid. A navbar is one row, so it is Flexbox. A whole page with a header, a sidebar, a main area and a footer is rows and columns together, so it is Grid.
How do you draw a page with template areas?
grid-template-areas lets you sketch the page as text. Each quoted string is one row, each word is one cell, and repeating a name across cells makes that area span. Here is a classic page skeleton in a 600px-wide container:
<div class="page">
<header>Header</header>
<aside>Sidebar</aside>
<main>Main content</main>
<footer>Footer</footer>
</div>.page {
display: grid;
grid-template-columns: 180px 1fr;
grid-template-rows: 60px 1fr 50px;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
width: 600px;
height: 400px;
}
header { grid-area: header; }
aside { grid-area: sidebar; }
main { grid-area: main; }
footer { grid-area: footer; }Rendered in Chromium 141, the numbers land exactly as the sketch promises. The header is 600 × 60px across the full width, because its name repeats across both columns. The sidebar is 180px wide and the main area is 420px wide, side by side: 180 + 420 = 600. The footer is 600 × 50px across the full width. The middle row takes the height that is left: 400 - 60 - 50 = 290px. Reading the grid-template-areas block tells you the whole page shape at a glance, which is why we reach for template areas first for page skeletons: the CSS reads like a sketch of the page.
Two details to remember. A . in the sketch means an empty cell, useful when part of the grid should stay blank. And every name in the sketch needs a matching grid-area rule on an element, spelled exactly the same. A typo does not produce an error message. The element quietly falls back to automatic placement and lands in the wrong cell, which is one of the common mistakes at the end of this tutorial.
What do fr units do?
fr means a fraction of the free space. In the skeleton above, the columns are 180px 1fr: the sidebar takes its fixed 180px, and the main area takes 1 fraction of whatever is left, which is 420px in a 600px container. Add a second flexible track, 180px 1fr 1fr, and the two 1fr tracks split the leftover space equally. Give one track 2fr and the other 1fr, and the first takes twice the share of the second.
The difference from percentages is that fr counts only the space left after fixed tracks and gaps. A 50% column next to a 180px sidebar takes 300px of the 600px, leaving only 120px for a third track; add a second 50% column and you need 780px, so it overflows. A 1fr column never overflows: it takes what remains. That is the whole point of the unit. Fixed sizes for things that must stay fixed, fr for everything that should share the rest.
What about rows?
Rows work the same way as columns, with grid-template-rows. The skeleton uses 60px 1fr 50px: a fixed header, a flexible middle that takes the remaining height, a fixed footer. Rows you do not declare are created automatically as content arrives, and they size to their content by default. Add gap: 12px and you get 12px of space between every row and every column at once, the same gap you already know from Flexbox. One caution: gap takes space out of the container before fr shares the rest, so three 1fr columns in a 600px container with a 12px gap are 192px each, not 200px: (600 - 24) / 3.
How do you build a gallery that reflows itself?
This is the pattern that makes Grid feel like magic the first time you see it, and it is one line:
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}Read it from the inside out. minmax(180px, 1fr) says each column must be at least 180px wide and can grow to fill the available space. repeat(auto-fit, ...) says fit as many of those columns as the container allows. Five items in a 600px-wide gallery render as three columns, each exactly 200px wide (600 / 3), with items 4 and 5 wrapping to row 2. The 200px is the 1fr stretching each track past its 180px minimum. Shrink the same gallery to a 380px container and it renders as two columns, each exactly 190px wide (380 / 2). Three 180px tracks would need 540px, which does not fit, so the third track is dropped and the remaining two stretch.
No media query was written. The column count changes with the container width because minmax is a floor, not a fixed width: the browser fits as many 180px tracks as it can, then 1fr shares the leftover space. This is the honest bridge to item 25 on responsive design. Media queries still have their place, for big changes like moving a sidebar below the content, but column counts no longer need them.
auto-fit or auto-fill: which one?
auto-fit and auto-fill look identical until the gallery has fewer items than the tracks it could fit. The difference is what happens to the empty tracks. auto-fit collapses them, so the items you do have stretch to fill the row. auto-fill keeps them, so the items stay at their narrower width and the empty tracks sit there as blank space at the end of the row.
Take a two-item gallery in a 600px container with the same minmax(180px, 1fr) rule. Three tracks could fit. With auto-fit, the empty third track collapses and the two items share the full width, 300px each by the same rule that gave 200px each for three. With auto-fill, the third track stays, the two items sit in the first two tracks, and the row ends with an empty column. Neither is wrong. This tutorial uses auto-fit because a gallery with two photos should fill the row, not leave a hole at the end. Use auto-fill when the empty slots matter, for example a calendar where blank days are part of the layout.
When do you still reach for Flexbox?
Grid does the page, Flexbox does the things inside each cell. The header in the skeleton above is a grid area, but the logo, links and button inside that header are one row, which is a Flexbox job. The sidebar is a grid area, but the list of links inside it is a Flexbox column. The gallery is a grid, but the title and price inside each gallery card are a Flexbox row with space-between.
We lay out the page with Grid and the things inside each cell with Flexbox, because each tool is doing the job it was built for. A grid item can be a flex container, and a flex item can be a grid container. Nesting them is normal, not a workaround, and it is how most real pages are built: Grid for the skeleton, Flexbox for the contents.
Putting it together: a page skeleton with a gallery inside
Here is the skeleton from this tutorial with the gallery living inside the main area. Same container, same areas, plus five gallery items:
<div class="page">
<header>Header</header>
<aside>Sidebar</aside>
<main>
<div class="gallery">
<div class="tile">1</div>
<div class="tile">2</div>
<div class="tile">3</div>
<div class="tile">4</div>
<div class="tile">5</div>
</div>
</main>
<footer>Footer</footer>
</div>.page {
display: grid;
grid-template-columns: 180px 1fr;
grid-template-rows: 60px 1fr 50px;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
width: 600px;
height: 400px;
}
header { grid-area: header; }
aside { grid-area: sidebar; }
main { grid-area: main; }
footer { grid-area: footer; }
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}Expected result, as rendered: header 600 × 60px, sidebar 180px beside a 420px main area, footer 600 × 50px, and inside the 420px main area the gallery shows two 210px columns on its first row. Shrink the whole page to 380px wide and the gallery drops to a single 200px column while the skeleton holds its shape. That is the whole tutorial in one example: areas for the page, fr for the sharing, auto-fit with minmax for the reflow.
Where to go next
- Pillar: Learn Frontend Development: The Complete Beginner Guide (/tutorials/frontend)
- Course: HTML & CSS Foundations (/course/html-css-foundations), lesson "CSS Grid Layouts in Practice" in the module "Layout and Responsive Design"
- Roadmap: Frontend Developer (/roadmap/frontend-developer), Weeks 1–3, "CSS styling, Flexbox and Grid", project "A personal portfolio website"
- Skills: CSS Grid (/skill/css-grid) (5h), CSS (/skill/css), CSS Flexbox (/skill/css-flexbox), Responsive Design (/skill/responsive-design)
- Previous in this series: CSS Flexbox in Depth: alignment, wrapping and ordering (/tutorials/frontend/css-flexbox-in-depth) (item 47, delivered with this pair)
- Next in this series: DOM Manipulation with JavaScript: selecting, changing and creating elements (/tutorials/frontend/dom-manipulation) (item 49, arrives with the next pair)
- Also in this series: CSS Flexbox vs Grid: which one, when (/tutorials/frontend/flexbox-vs-grid) (item 19) and Responsive Design with Media Queries: a worked example (/tutorials/frontend/responsive-design-media-queries) (item 25)
Try it yourself
Build the page skeleton from this tutorial, then add the gallery inside the main area. Shrink the container from 600px to 380px and watch the gallery drop from two columns to one with no media query. Then change auto-fit to auto-fill, remove three gallery items so only two remain, and compare: the items stay narrow and an empty third column appears. Change it back to auto-fit and the two items stretch to fill the row. Finally, rename one area in grid-template-areas by one letter, reload, and watch that element jump to the wrong cell. Rename it back.
Common mistakes
- A typo in an area name. The element has no matching
grid-area, so it falls back to automatic placement and lands in the wrong cell, with no error message. Area names must match exactly. - Unequal cell counts in
grid-template-areas. Every quoted row must name the same number of columns. If one row is shorter, the whole declaration is ignored and the grid falls back to automatic placement. - A
minmaxminimum wider than a phone.minmax(300px, 1fr)on a 320px screen leaves one cramped column. Keep the minimum small, 180px works for cards, so narrow screens still get a usable layout. - Fixed pixel columns that overflow small screens. Four 250px columns need 1000px and will overflow a phone. Use
frandminmaxso tracks share the space that exists. - Using
auto-fillwhen you wantedauto-fit. A short gallery ends with an empty track at the end of the row. If the row should always look full, useauto-fit.
Frequently asked questions
Grid or Flexbox for a navbar?
Flexbox. A navbar is one row of items, which is exactly the one-direction job Flexbox was built for. Grid would work, but it adds rows and columns you do not need.
Can I nest Grid inside Flexbox, and the other way round?
Yes. A grid item can be a flex container and a flex item can be a grid container. Real pages do this constantly: Grid for the page skeleton, Flexbox for the contents of each cell.
Why did my grid-area name do nothing?
Either the element has no grid-area rule, or the name is spelled differently from the name in grid-template-areas. The two must match exactly, including case, or the element falls back to automatic placement.
Do I still need media queries with auto-fit?
Sometimes. auto-fit handles column counts on its own, but bigger changes still need a media query: moving the sidebar below the main content on a phone, for example, or changing the whole grid-template-areas sketch at a breakpoint. Item 25 covers media queries in detail.
What happens if I name an area in grid-template-areas but no element uses it?
Nothing breaks. The area stays empty and the grid keeps its shape. That is how you leave a deliberate blank cell, along with the . placeholder.
Follow the Frontend Developer Roadmap
Build modern, responsive websites and web apps with HTML, CSS, JavaScript and React. Free, step by step, at your pace.
View the Roadmap