Fixing CSS Grid Auto Rows — Implicit Track Sizing Explained
Quick answer
💡grid-auto-rows controls the height of rows created automatically when grid items are placed beyond your defined grid-template-rows. Set grid-auto-rows: minmax(100px, auto) to give implicit rows a minimum height while letting them grow to fit their content. Without it, implicit rows collapse to the minimum content height.
Error symptoms
- ✕
Rows collapse to zero height or become very thin unexpectedly - ✕
Grid items added dynamically fall into rows that are too short - ✕
Rows defined in grid-template-rows work but additional rows do not - ✕
grid-auto-rows value appears in the stylesheet but has no visible effect - ✕
Items placed with grid-row: span 2 create implicit rows of the wrong height - ✕
Dense packing leaves unexpected gaps or row size inconsistency
Common causes
- •grid-template-rows defines explicit rows but no grid-auto-rows for overflow items
- •Items placed beyond the last explicit row enter the implicit grid with default auto height
- •Using grid-auto-rows: 1fr when the grid container has no explicit height
- •minmax() minimum set too high causes overflow on short-content items
- •grid-auto-flow: dense moving items reorders them into implicit rows
- •Subgrid not supported in older browsers produces unexpected fallback sizing
When it happens
- •Rendering dynamic card lists where item count is unknown at build time
- •Building photo galleries where grid auto-placement fills irregular spaces
- •Creating dashboards with widgets of varying heights placed by JavaScript
- •When a CMS generates grid children and the template rows run out
Examples and fixes
Only three rows are defined but a fourth item is added, creating a collapsed implicit row.
Implicit rows collapsing to zero height
❌ Wrong
<div class="gallery">
<img src="photo-1.jpg" alt="Photo 1">
<img src="photo-2.jpg" alt="Photo 2">
<img src="photo-3.jpg" alt="Photo 3">
<img src="photo-4.jpg" alt="Photo 4">
</div>
/* CSS */
.gallery {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: 200px 200px;
gap: 1rem;
}✅ Fixed
<div class="gallery">
<img src="photo-1.jpg" alt="Photo 1">
<img src="photo-2.jpg" alt="Photo 2">
<img src="photo-3.jpg" alt="Photo 3">
<img src="photo-4.jpg" alt="Photo 4">
</div>
/* CSS */
.gallery {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: 200px 200px;
grid-auto-rows: 200px;
gap: 1rem;
}The two grid-template-rows define row 1 and row 2. Photo 3 and Photo 4 fit into those rows. If a fifth or sixth image is added, it enters the implicit grid — row 3 and beyond. Without grid-auto-rows, implicit rows take their height from the auto track sizing algorithm, which resolves to the minimum content height of the items. For images that have not loaded yet, this may be zero. Adding grid-auto-rows: 200px gives every implicit row the same fixed height as the explicit rows.
Use minmax to set a minimum row height while allowing taller content to expand the row.
Flexible rows that grow with content
❌ Wrong
<div class="dashboard">
<div class="widget">Short content</div>
<div class="widget">Very long content that wraps across multiple lines and needs more vertical space than a fixed height allows</div>
</div>
/* CSS */
.dashboard {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 150px;
gap: 1.5rem;
}✅ Fixed
<div class="dashboard">
<div class="widget">Short content</div>
<div class="widget">Very long content that wraps across multiple lines and needs more vertical space than a fixed height allows</div>
</div>
/* CSS */
.dashboard {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(150px, auto);
gap: 1.5rem;
}grid-auto-rows: 150px forces every implicit row to exactly 150px. A widget with more content than fits in 150px will overflow the row and overlap the row below it. Changing to minmax(150px, auto) sets 150px as the minimum — rows that contain only short content stay at 150px, while rows containing longer content expand to fit their tallest item. This keeps the grid looking intentional regardless of dynamic content length.
An item spanning 2 rows can push later items into the implicit grid with unintended heights.
Spanning items creating implicit rows
❌ Wrong
<div class="card-grid">
<div class="card card--featured">Featured</div>
<div class="card">Card 2</div>
<div class="card">Card 3</div>
<div class="card">Card 4</div>
</div>
/* CSS */
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 250px;
gap: 1rem;
}
.card--featured {
grid-row: span 2;
}✅ Fixed
<div class="card-grid">
<div class="card card--featured">Featured</div>
<div class="card">Card 2</div>
<div class="card">Card 3</div>
<div class="card">Card 4</div>
</div>
/* CSS */
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 250px;
grid-auto-rows: 250px;
gap: 1rem;
}
.card--featured {
grid-row: span 2;
}The featured card spans 2 rows. Row 1 is defined at 250px by grid-template-rows. Row 2 is an implicit row because only one explicit row is defined. Without grid-auto-rows, row 2 collapses to the minimum content height of whatever items land in it. Adding grid-auto-rows: 250px makes row 2 match row 1 in height, and the featured card spans the intended 500px total.
How implicit grid rows work
CSS Grid divides tracks into two categories: explicit and implicit. Explicit rows are those you define with grid-template-rows. The implicit grid is everything beyond those defined tracks — rows and columns created automatically by the browser when grid items are placed outside the explicit area.
By default, implicit rows use the auto keyword for sizing. Auto means the row height is determined by the content of the tallest item in that row. For images that are loading or for items with no intrinsic height, auto can resolve to very small or zero values. This is the reason dynamic grids often look broken when content is added at runtime — the explicit rows look correct, but every additional row collapses unpredictably.
grid-auto-rows changes the sizing algorithm for all implicit rows at once. It accepts the same values as grid-template-rows: fixed lengths like 200px, the auto keyword, the fr unit, or minmax() with any combination of these. You only need one grid-auto-rows declaration on the container — it applies to every implicit row the grid creates.
The fr unit in grid-auto-rows behaves differently from fr in grid-template-rows. A 1fr value in grid-auto-rows means one fraction of the remaining space after fixed tracks are accounted for — but only if the grid container has an explicit height. Without a defined container height, 1fr in grid-auto-rows resolves to auto, the same as not writing grid-auto-rows at all. This is one of the most common reasons grid-auto-rows: 1fr appears to have no effect.
Understanding the interaction between grid-template-rows and grid-auto-rows is essential. grid-template-rows defines row sizes at specific positions — row 1, row 2, and so on. Once all explicitly defined rows are consumed by grid items, every subsequent row is an implicit row sized by grid-auto-rows. This division means you can mix explicit rows with special heights for a header or featured item, then let grid-auto-rows handle everything below uniformly.
Finding implicit row problems in DevTools
Open Chrome DevTools and select the grid container. In the Elements panel, a green grid badge appears next to the element. Click the badge to enable the grid overlay — it draws grid lines, row and column numbers, and track labels directly over the page. Implicit rows are numbered differently from explicit rows: explicit rows go from 1 to N, implicit rows extend the numbering outward.
In the Layout pane, find the Grid section and enable row line numbers. This shows the full grid structure including implicit tracks. A collapsing row appears as an extremely thin slice in the overlay. Hover over the row number to see its computed size in the tooltip.
In the Computed tab, look for grid-auto-rows in the element's styles. If it shows auto or is absent, implicit rows are using content-based sizing. Check the computed height of the grid container — if it is auto with no explicit value, 1fr in grid-auto-rows will resolve to auto as well.
For Firefox, the Grid Inspector in DevTools is equally capable. Enable the Grid overlay from the Inspector tab and check the Track Sizes section in the Layout panel. Firefox shows a clear distinction between explicit and implicit track sizes with different formatting.
Chrome's grid overlay uses solid lines for explicit tracks and dashed lines for implicit tracks. This visual distinction is the fastest way to determine whether a row is controlled by grid-template-rows or by grid-auto-rows. If you expected a row to be explicit but the overlay shows a dashed line, that row has fallen into the implicit grid — either because more items were placed than rows defined, or because a spanning item pushed placement beyond the explicit boundary. Identifying dashed versus solid lines eliminates guesswork about which sizing rule applies to a given row.
You can also hover over individual grid cells in the overlay to see the computed start and end line numbers for each grid item. If an item shows a row-end line number greater than the total number of explicit rows, it or a sibling is creating implicit rows whose height depends entirely on grid-auto-rows.
The right grid-auto-rows value for each case
For fixed-height rows where all items should be the same height, use a pixel value: grid-auto-rows: 200px. This is the simplest fix when your design calls for uniform row heights and all content fits within that height. Pair it with overflow: hidden on grid items to prevent content from escaping the track.
For content that varies in length, use minmax: grid-auto-rows: minmax(120px, auto). The first argument is the minimum height — every row will be at least this tall even if the content is short. The second argument auto allows the row to grow when content is taller than the minimum. This is the most flexible and content-safe value for dynamic grids.
For masonry-style grids where items should fill the shortest column, grid-auto-rows does not help — masonry layout requires CSS Masonry (behind a flag in Firefox) or JavaScript. As an approximation, use grid-auto-rows: minmax(10px, auto) with very short rows and position items with grid-row-end: span N, where N is proportional to the item height.
For dashboards or admin panels where section heights are unknown, grid-auto-rows: auto is often the safest choice. It means each row is as tall as its tallest cell. If you have defined grid-template-rows for a header or footer, only the dynamic middle section needs grid-auto-rows: auto to handle overflow items gracefully without collapsing.
When a grid needs consistent row heights across many dynamic items, grid-auto-rows also accepts the repeat() notation indirectly through grid-template-rows. Defining grid-template-rows: repeat(3, 200px) followed by grid-auto-rows: 200px ensures rows 1 through 3 are explicit and identically sized, while every subsequent row inherits the same 200px height from grid-auto-rows. This combination is more explicit than relying on grid-auto-rows alone and makes the intended row structure clear to future developers reading the stylesheet.
Edge cases with auto-flow and spanning
When grid-auto-flow: dense is enabled, the browser fills gaps left by spanning items by moving later items backward in source order. This can cause items with grid-auto-rows heights to appear in unpredictable rows because the dense algorithm finds the first available gap and places items there regardless of DOM order. The row height is determined by the items that land in each row after dense packing runs.
Items that use grid-row: span 2 create an implicit second row if only one explicit row is defined. The implicit row height comes from grid-auto-rows. If grid-auto-rows is not set, the second half of the spanning item sits in an auto-height row that collapses to the minimum content of other items in that row. The fix is always to set grid-auto-rows to match the intended height of explicit rows.
Subgrid, supported in Chrome 117+ and Firefox 71+, allows children of a grid item to participate in the parent's row and column tracks. When children use subgrid, their implicit rows are not controlled by grid-auto-rows on the parent — the parent's explicit tracks determine the available rows. If children overflow the parent's explicit tracks, they exit the subgrid and enter the regular implicit grid.
On mobile, a grid-auto-rows value set in pixels may create rows that are too tall for narrow viewports. Use clamp() for responsive row heights: grid-auto-rows: clamp(100px, 25vw, 250px) sets a minimum, a viewport-relative target, and a maximum. Alternatively, use auto and let rows size to content at all widths.
The dense packing keyword also interacts with named grid areas. If an item is assigned to a named area and dense mode is active, the browser still respects the named area placement but may backfill surrounding gaps with non-named items. This can create rows where named and auto-placed items coexist, and the row height is determined by the tallest of all items placed in that row regardless of whether they are named or auto-placed.
Mistakes with implicit row sizing
Using grid-auto-rows: 1fr without giving the grid container an explicit height is the most common mistake. When the container height is auto, 1fr has nothing to be a fraction of, so it resolves to auto. The rows end up content-sized regardless of the 1fr value. Either add height: 600px to the container, or use a pixel or minmax value instead.
Confusing grid-template-rows and grid-auto-rows is also frequent. grid-template-rows defines specific row sizes at specific positions: the first value is row 1, the second is row 2, and so on. grid-auto-rows defines the size for all rows that are created beyond those. If you want all rows — including the first three you defined — to be 200px, you need both: grid-template-rows: repeat(3, 200px) and grid-auto-rows: 200px for any additional rows.
Setting grid-auto-rows on a grid item instead of the grid container has no effect. grid-auto-rows is a container property only. If you accidentally write it on a div that is not the grid container, or on an element that has display: block instead of display: grid, the browser silently ignores it.
Not accounting for gap in height calculations causes layout overflow. If grid-auto-rows: 200px and the container is 410px with one row of gap: 10px, the two rows of 200px plus one gap of 10px total 410px exactly. Adding a third row pushes total content height to 620px plus two gaps — larger than the container. Use overflow: auto on the container or use minmax with auto to let the container grow.
Best practices for grid row sizing
Always set grid-auto-rows whenever you use grid-template-rows. Any grid template that defines explicit rows will eventually receive more items than rows defined, especially when driven by CMS data or API responses. Treating grid-auto-rows as an optional extra leads to collapsing rows appearing in production when the edge case is first encountered.
Prefer minmax(minimum, auto) over fixed-pixel row heights for content-driven grids. A fixed pixel height will clip or overflow dynamic content; minmax sets a floor and lets the row grow safely. Reserve fixed pixel heights for grids where you control all content, like a data table with one-line rows.
Test with more items than your template defines. If grid-template-rows has three rows, test your layout with six or nine items to ensure implicit rows look intentional. Add a browser test or a Storybook story that renders the grid with N+1, N+2, and N+3 items to catch regressions.
For responsive grids, combine auto-fit columns with minmax rows: grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); grid-auto-rows: minmax(180px, auto). This creates a layout that adjusts column count to screen width while keeping row heights consistent and content-safe. No media queries are needed for the grid structure itself.
Document the expected number of explicit rows and the purpose of grid-auto-rows in a comment. A comment like row 1-3 defined explicitly; additional rows auto-sized to match prevents a future developer from removing grid-auto-rows assuming it is redundant with grid-template-rows.
For grids where all rows should behave identically regardless of whether they are explicit or implicit, skip grid-template-rows entirely and rely solely on grid-auto-rows. This avoids the risk of explicit and implicit rows diverging in size when content changes. If only the first row needs a special height — such as a featured banner item — define just that row in grid-template-rows and let grid-auto-rows control everything below it uniformly.
Quick fix checklist
- ✓Enable the Grid overlay in DevTools to see implicit row heights visually
- ✓Confirm grid-auto-rows is on the grid container, not a grid item
- ✓If using 1fr, add an explicit height to the grid container
- ✓Use minmax(minimum, auto) for grids with variable-length content
- ✓Test with more items than your grid-template-rows defines
- ✓Set grid-auto-rows to match explicit row heights for visual consistency
- ✓Check that gap is not causing total height to exceed the container
- ✓For spanning items, verify implicit rows are sized by grid-auto-rows correctly
Related guides
Frequently asked questions
What is the difference between grid-template-rows and grid-auto-rows?
grid-template-rows explicitly defines the height of each row by position — first value is row 1, second is row 2, and so on. grid-auto-rows sets the height for all rows the browser creates automatically beyond the explicit template. Both can coexist on the same container and serve complementary roles.
Why does grid-auto-rows: 1fr have no effect?
1fr in grid-auto-rows requires the grid container to have an explicit height. When the container height is auto (determined by content), 1fr cannot be computed as a fraction of anything and falls back to auto. Add height: 500px or another explicit height to the container, or use a pixel or minmax value instead.
How do I make implicit rows the same height as explicit rows?
Set grid-auto-rows to the same value as your grid-template-rows entries. If your template rows are 200px, set grid-auto-rows: 200px. If your template rows use minmax, use the same minmax value. This ensures every row — explicit and implicit — follows the same sizing rule.
Can I use minmax() in grid-auto-rows?
Yes, and it is strongly recommended for dynamic content. minmax(100px, auto) sets 100px as the minimum row height and auto as the maximum, so the row grows to fit its tallest item. This is safer than a fixed pixel value because it prevents content from clipping when items contain more text than expected.
Why do rows added by JavaScript look different from rows in my template?
Rows rendered by JavaScript beyond your grid-template-rows count enter the implicit grid. If grid-auto-rows is not set, they size to their content minimum which may be different from your explicit rows. Set grid-auto-rows on the container to apply consistent sizing to all dynamically added rows.
How does grid-auto-flow: dense interact with grid-auto-rows?
grid-auto-flow: dense reorders items to fill visual gaps, which may place items in different rows than their DOM order. grid-auto-rows still applies the same height to each implicit row regardless of which item lands in it. The interaction to watch is spanning items — a span-2 item with dense packing can end up in unexpected implicit rows with sizes controlled by grid-auto-rows.
Does grid-auto-rows work with CSS subgrid?
When a grid item uses grid-template-rows: subgrid, its children participate in the parent grid's explicit tracks. grid-auto-rows on the parent controls only the implicit tracks of the parent grid. Children that overflow the parent's subgrid tracks exit the subgrid and are placed by the normal implicit grid algorithm.
How do I create responsive row heights without media queries?
Use clamp() in grid-auto-rows: grid-auto-rows: clamp(120px, 20vw, 280px). This gives rows a viewport-relative height that scales between a fixed minimum and maximum. Alternatively, use grid-auto-rows: minmax(auto, 1fr) with an explicit container height to distribute available space evenly across all rows.
All tools run in your browser. Your data never leaves your device. Last updated: 2026-05-06.