07 · Grid Utilities¶
Grid is the layout tool for anything with rows and columns: galleries, dashboards, page shells, forms with aligned labels. Tailwind's grid utilities cover the everyday cases with short names and let you drop to any CSS grid template with an arbitrary value when you need more. (For Grid itself, see CSS Grid Fundamentals and Grid in Depth.)
Columns and rows¶
.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-rows-subgrid { grid-template-rows: subgrid; }
.auto-rows-fr { grid-auto-rows: minmax(0, 1fr); }
(From the compiler.) grid-cols-{n} works for any number, grid-cols-none removes the
template, and grid-rows-{n} does the same for rows.
Why minmax(0, 1fr) matters — measured¶
Tailwind deliberately generates minmax(0, 1fr) rather than plain 1fr. Plain 1fr has an
automatic minimum of the content's minimum width, so one long unbreakable word can widen its
column. Three columns in a 600px grid, the middle one containing a long word:
grid-cols-3 kept the columns equal (the long word overflows its cell instead — pair with
wrap-anywhere on content that might contain long strings). The hand-written
repeat(3,1fr) let the content bully the layout. It's a good default to have.
Spanning and placing¶
<div class="grid grid-cols-4 gap-4">
<div class="col-span-2 row-span-2">Feature</div>
<div class="col-start-4">Pinned to column 4</div>
<div class="col-span-full">Full width</div>
</div>
Also col-start-{n}, col-end-{n}, row-start-{n}, row-end-{n}, and grid-flow-row,
grid-flow-col, grid-flow-dense for the auto-placement direction.
Arbitrary templates¶
Tailwind's named templates cover equal columns; everything else is an arbitrary value. Inside square brackets, underscores become spaces:
<div class="grid grid-cols-[200px_1fr]">sidebar + content</div>
<div class="grid grid-cols-[repeat(auto-fit,minmax(15rem,1fr))] gap-6">responsive gallery</div>
<div class="grid grid-rows-[auto_1fr_auto] min-h-dvh">header / main / footer</div>
These compile to exactly what you wrote:
.grid-cols-\[200px_1fr\] { grid-template-columns: 200px 1fr; }
.grid-cols-\[repeat\(auto-fit\,minmax\(15rem\,1fr\)\)\] { grid-template-columns: repeat(auto-fit,minmax(15rem,1fr)); }
The auto-fit gallery is the most useful arbitrary template in practice: as many columns
as fit, each at least 15rem, no media queries. Add min(15rem,100%) inside the minmax
to stop it overflowing containers narrower than 15rem.
If you use a template more than once or twice, name it in the theme instead (Level 2 · 01)
so it becomes grid-cols-gallery.
Subgrid¶
grid-rows-subgrid and grid-cols-subgrid let a child share its parent's tracks — the
standard fix for cards whose titles, bodies and buttons should line up across a row:
<ul class="grid grid-cols-1 gap-6 md:grid-cols-3">
<li class="row-span-3 grid grid-rows-subgrid gap-2 rounded-lg border border-gray-200 p-4">
<h3 class="font-semibold">Short title</h3>
<p class="text-sm text-gray-600">Body text of any length…</p>
<a class="self-end text-sm font-medium text-sky-700" href="/a">Read more</a>
</li>
<!-- more cards -->
</ul>
Each card spans three of the parent's rows and uses them for its three children, so every card's link sits at the same height.
Alignment¶
place-items-center, justify-items-start, items-center, justify-self-end,
self-start, place-content-center — the same vocabulary as Flexbox, applied to grid
cells. place-items-center on a one-cell grid is the shortest way to centre something.
Worked example: a page shell¶
<body class="grid min-h-dvh grid-rows-[auto_1fr_auto]">
<header class="border-b border-gray-200 p-4">…</header>
<div class="grid md:grid-cols-[16rem_1fr]">
<nav class="border-e border-gray-200 p-4 max-md:hidden" aria-label="Sections">…</nav>
<main class="min-w-0 p-6">…</main>
</div>
<footer class="border-t border-gray-200 p-4 text-sm text-gray-500">…</footer>
</body>
Three rows (header and footer sized to content, the middle taking the rest), and from
md up a fixed 16rem sidebar beside the content. min-w-0 on <main> stops wide
content such as tables or code from stretching the 1fr column — the same minimum-size
issue as in Flexbox, because 1fr in an arbitrary template is not minmax(0, 1fr).
How It Actually Works¶
For grid-cols-{n}, the compiler takes the numeric value and generates
repeat(n, minmax(0, 1fr)) — a template, not a lookup, which is why any number works.
Arbitrary values go through a small CSS value decoder: the bracket contents are taken
literally, underscores are turned into spaces (write \_ for a real underscore), and the
selector is escaped so that characters like (, , and [ are valid in a class
selector — hence the heavily escaped .grid-cols-\[repeat\(auto-fit\,…\)\]. Tailwind
doesn't validate your grid syntax beyond basic structure; an invalid template produces a
CSS declaration the browser simply ignores, so if an arbitrary template "does nothing",
check the Computed pane for the property.
Common mistakes¶
grid-cols-[repeat(3,1fr)]wheregrid-cols-3would give the saferminmax(0,1fr).- Spaces inside brackets —
grid-cols-[200px 1fr]is two separate (invalid) class names. Use underscores. - Forgetting
min-w-0on a1frcolumn holding wide content. - Too many arbitrary templates instead of named theme values.
- Grid for a single row of items that should wrap by content — Flexbox with
flex-wrapis simpler.
Exercise¶
- Build a responsive gallery with
grid-cols-[repeat(auto-fill,minmax(min(14rem,100%),1fr))]. Test with 2 and 9 items; then switch toauto-fitand compare. - Build the page shell. Put a wide table in
<main>, then removemin-w-0and see what happens at 800px wide. - Make a 4-column grid where the first card spans two columns and two rows.
- Build three cards with subgrid and confirm the links align when titles differ in length.