For a CSS grid rendering a list whose length comes from an API, would you declare the rows with grid-template-rows or let the implicit grid create them, and what breaks with the wrong choice?
answer
- who owns the count, you or the data
- declare one axis, let the other grow
- guessing wrong fails in both directions
- empty declared tracks still cost space
- named areas need a known row count
basics
~20 sDeclare only the axis you control — usually the columns — and let the rows be implicit, sizing them with grid-auto-rows. Declaring a row count guesses at the data: too few rows and implicit ones appear anyway with a different size, too many and the empty declared rows still occupy space and gaps.
solid answer
~40 sI declare `grid-template-columns` because the column structure is a design decision I own, and I let the rows be implicit because their count belongs to the data. I then size them once with `grid-auto-rows: minmax(180px, auto)` so every row has the same floor and can still grow. Hardcoding `grid-template-rows` fails in both directions: undercount and the extra rows are implicit anyway, sized by a different property, so the last rows look different from the first ones; overcount and the surplus declared rows still exist — a fixed-size one holds its height and every one of them contributes a gap, leaving dead space under the content. Declared rows are also what `grid-template-areas` needs, so an areas-based layout does not stretch to unknown item counts; that is a signal the layout should be columns-plus-implicit-rows instead.
code
css · 6 lines.results {
display: grid;
grid-template-columns: 240px 240px 240px;
grid-auto-rows: minmax(180px, auto);
gap: 16px;
}go deeper
Know that you normally declare the columns and let rows be created as needed, and that grid-auto-rows is where the height of those rows is set.
Explain both failure modes of a hardcoded row count — a mismatched last row when you undercount, occupied empty tracks and their gaps when you overcount.
Show the judgment of matching the declared axis to what the design actually owns, and test the layout at zero, one and overflow item counts rather than only at the fixture count.
Treat the row count as part of the component's contract: encoding a data-volume assumption in a stylesheet is invisible to consumers, so decide where that assumption is allowed to live and how it is reviewed.
## The decision, stated plainly Declare the axis whose size the design owns. Let the other axis be implicit. For a card list or a search-results page that means: columns are yours, rows belong to the data. ```css .results { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); grid-auto-rows: minmax(180px, auto); gap: 16px; } ``` There is no row count anywhere in that rule, and that is the point: nothing in the stylesheet has to be edited when the API returns eleven items instead of six. ## Failure mode one — too few declared rows Suppose someone writes `grid-template-rows: 180px 180px` because the mock showed six cards in three columns. On a page with nine cards, rows 1 and 2 are explicit at 180px and row 3 is implicit at `auto`. The bottom row now hugs its content and visibly breaks the rhythm. The bug is intermittent by construction: it appears only for item counts that overflow the declared rows, so it survives review, survives the fixture data, and shows up in production. The deeper problem is that two properties are now responsible for the same visual rule. Someone changing the card height edits `grid-template-rows` and misses `grid-auto-rows`, or the other way round, and the seam reopens. ## Failure mode two — too many declared rows The defensive move is worse: `grid-template-rows: repeat(10, 180px)` so there is "always enough". Explicit tracks exist regardless of whether anything is in them. With three cards you get one filled row and nine empty 180px rows plus their gaps — roughly 1800px of blank space below the content, with the footer pushed off screen. Making them `auto` instead does not fully save you either: an empty `auto` row collapses to zero height, but the gaps between tracks are still drawn, so a long over-declared list leaves a visible band of spacing. ## The areas trap Named areas are the other reason people declare rows: ```css .page { display: grid; grid-template-areas: "head head" "side main" "foot foot"; grid-template-columns: 200px 1fr; } ``` This is exactly right for a page chrome, where the number of regions is fixed and structural. It is exactly wrong for a feed, because the area strings enumerate rows: to hold n items you would need n strings, and you do not know n. Any item beyond the named grid is auto-placed into an implicit row, outside every named area, and stops obeying the layout the areas were written to enforce. "Can I name every region?" is a good test for which style of grid you are building. ## Where declaring rows is still correct The rule is not "never declare rows". Declare them when their count is structural rather than data-driven, and when the sizes genuinely differ: ```css .panel { display: grid; grid-template-rows: auto 1fr auto; /* header, body, footer */ height: 100%; } ``` Three rows that mean three different things, a count that cannot change with the data — declaring them communicates intent, and implicit rows should never appear here at all. If one does, it is a bug signal: something was placed outside the structure you designed. The mixed form is also legitimate when the first rows are genuinely special: a taller featured row followed by uniform ones is `grid-template-rows: 280px` plus `grid-auto-rows: 180px`, and the difference between the two values is intentional rather than accidental. ## What to check in review Three questions catch most of these. Does the row count in the stylesheet correspond to anything the component actually guarantees? If yes, does an empty instance of that layout look right — zero items, one item? And are the explicit and implicit sizing rules saying the same thing when they are supposed to, or has a seam been left where two properties both control what should be one rule? ## How to say it in an interview Lead with the principle — declare what you own, let the data-driven axis grow — then name both failure modes concretely, because that is what shows you have shipped this rather than read about it: undercount produces a mismatched last row, overcount produces dead space that no amount of content will fill.
- What actually happens to a declared row that ends up with nothing in it?It still exists. A fixed-size empty row occupies its full size, and an `auto` empty row collapses to zero height — but in both cases the gaps on either side of it are still drawn. That is why over-declaring rows leaves visible dead space rather than harmlessly disappearing.
- You said an areas-based layout does not stretch to unknown item counts — why not?Because each string in `grid-template-areas` is one declared row, so the value enumerates the row count up front. Items beyond that grid are auto-placed into implicit rows that belong to no named area, so they escape the structure the areas were written to enforce.
- When would you still declare rows explicitly?When the count is structural and the sizes differ — a panel of header, body and footer as `auto 1fr auto`, for instance. Those three rows mean three different things and cannot change with the data, so declaring them communicates intent, and an implicit row appearing there is a bug signal rather than normal growth.
saying these in an interview costs you the question
- Declares a large repeat() row count to be safe
- Assumes an empty declared row disappears entirely
- Thinks grid-template-areas can grow with the data
- Sets grid-template-rows and grid-auto-rows to different values by accident
- Only tests the layout with the exact fixture item count