How CSS grid-auto-flow: dense Fills Grid Holes
CSS Grid provides a powerful auto-placement algorithm to position
elements without requiring explicit coordinates for every item. When
grid items have varying sizes or span multiple tracks, the default
layout often creates awkward, empty spaces. The
grid-auto-flow: dense property solves this issue by
instructing the browser to backtrack through the grid and pack smaller
elements into previously bypassed holes, producing a compact and
gap-free layout.
The Problem: Default Auto-Placement
By default, CSS Grid uses grid-auto-flow: row (or
grid-auto-flow: column). Under this behavior, the browser
processes items strictly in the order they appear in the Document Object
Model (DOM).
When the browser encounters an item that spans multiple columns or rows and cannot fit into the current available space, it leaves that space empty and moves the large item to the next available track. Crucially, the standard algorithm only moves in a forward direction; it will never look backward to fill the gap it just left, even if later items in the source order are small enough to fit. This results in unintended empty "holes" across the grid.
What is
grid-auto-flow: dense?
The dense keyword modifies the placement algorithm by
enabling a "dense" packing mode. Instead of strictly preserving DOM
sequence along the grid tracks, grid-auto-flow: dense
allows the browser to reorder items visually to optimize space
utilization.
You can declare it on a grid container using either of the following syntaxes:
.grid-container {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-flow: dense; /* Defaults to row dense */
}
/* Or explicitly set the flow direction alongside dense */
.grid-container {
grid-auto-flow: row dense;
/* or */
grid-auto-flow: column dense;
}How It Fills Empty Layout Holes
When dense is enabled, the browser handles placement
through a dynamic search process:
- Space Allocation: As the browser encounters large
items (e.g., an element set to
grid-column: span 2), it attempts to place them. If the current row does not have enough remaining space, the item shifts to the next row, creating a gap behind it. - Backtracking: When subsequent items are processed, the layout engine does not automatically append them after the most recently placed item. Instead, it scans the entire grid from the start to identify the first open slot where the current item can physically fit.
- Slot Filling: If a subsequent item has a small footprint (such as a standard 1x1 cell item), and a preceding gap can accommodate its dimensions, the browser places the item into that earlier gap.
This mechanism ensures that empty cells created by oversized items are populated by any later, smaller elements found down the line in the HTML markup.
Practical Use Cases
- Image Galleries: Dynamic photo grids with a mix of portrait, landscape, and panoramic images can achieve a balanced, masonry-style look without empty cells.
- Dashboard Widgets: Modular dashboards containing cards of varying dimensions (1x1, 2x1, 2x2) can pack tightly without manual positioning.
- Product Listings: Highlighting specific "featured" products with larger spans while keeping the overall grid compact.
Accessibility and Usability Considerations
While grid-auto-flow: dense creates visually seamless
layouts, it alters the visual presentation relative to the DOM source
order.
Because keyboard navigation (such as the Tab key) and
screen readers follow the DOM order rather than the visual layout,
navigating a dense grid can cause the focus ring to jump erratically
across the screen. For this reason, grid-auto-flow: dense
should primarily be used for non-sequential content where reading order
and tab order are not critical to the user experience.