Grid

  1. Posted No date
  2. Updated Ongoing
  3. Ends Ongoing

Props reference

Grid

PropTypeDefaultDescription
cols1–12 | "auto" | "masonry"1Column count. "auto" = fluid fill. "masonry" = CSS columns.
rows1–12 | "auto""auto"Explicit row count. Leave "auto" unless you need a fixed grid.
gap"none"|"xs"|"sm"|"md"|"lg"|"xl"|"2xl""md"Uniform gap.
gapXsame scaleHorizontal gap override.
gapYsame scaleVertical gap override.
align"start"|"center"|"end"|"stretch""stretch"align-items
justify"start"|"center"|"end"|"stretch"|"between"|"around"|"evenly""stretch"justify-items / justify-content
flow"row"|"col"|"dense"|"row-dense"|"col-dense""row"grid-auto-flow
minColWidthstring"240px"Minimum column width for cols="auto".
depthnumber0Recursion depth. Auto-incremented mentally — pass manually if needed.
asstring"div"HTML element to render.
fullbooleantrueAdds w-full.
prosebooleanfalseAdds max-w-prose mx-auto for text layouts.
classstringExtra Tailwind classes.

GridItem

PropTypeDefaultDescription
colSpan1–12 | "full" | "auto""auto"Column span.
rowSpan1–6 | "full" | "auto""auto"Row span.
colStart1–13 | "auto""auto"Explicit column start line.
colEnd1–13 | "auto""auto"Explicit column end line.
rowStart1–7 | "auto""auto"Explicit row start line.
rowEnd1–7 | "auto""auto"Explicit row end line.
align"start"|"center"|"end"|"stretch""stretch"align-self
justify"start"|"center"|"end"|"stretch""stretch"justify-self
asstring"div"HTML element.

Recipes

1. Simple 3-column card grid

<Grid cols={3} gap="lg">
  <Card title="One" />
  <Card title="Two" />
  <Card title="Three" />
</Grid>

Responsive: collapses → 2 cols at sm → 1 col on mobile.


2. Fluid auto-fill (Pinterest-style)

<Grid cols="auto" minColWidth="280px" gap="md" align="start">
  {posts.map((p) => <PostCard entry={p} />)}
</Grid>

Fills as many columns as fit at 280px minimum.


3. Masonry (CSS columns)

<Grid cols="masonry" gap="md" class="columns-1 sm:columns-2 lg:columns-3">
  {items.map((i) => <Item data={i} />)}
</Grid>

Uses CSS columns rather than grid. Items flow top-to-bottom through columns. Override column count with Tailwind columns-* via class.


4. Feature layout — spanning hero cell

<Grid cols={3} gap="lg">
  <GridItem colSpan={2} rowSpan={2}>
    <HeroCard />
  </GridItem>
  <SmallCard />
  <SmallCard />
</Grid>

5. Explicit placement (magazine layout)

<Grid cols={4} rows={3} gap="sm" flow="dense">
  <GridItem colStart={1} colSpan={2} rowStart={1} rowSpan={2}>
    <Feature />
  </GridItem>
  <GridItem colStart={3} colSpan={2}>
    <Article />
  </GridItem>
  <GridItem colSpan="full" rowStart={3}>
    <Banner />
  </GridItem>
</Grid>

flow="dense" backfills gaps automatically.


6. Recursive nested grids

<Grid cols={2} gap="xl">
  {/* Left: nested 2×2 sub-grid */}
  <Grid cols={2} gap="sm" depth={1}>
    <Card /><Card />
    <Card /><Card />
  </Grid>

  {/* Right: single tall card */}
  <GridItem align="stretch">
    <TallCard />
  </GridItem>
</Grid>

Nested Grid at depth={1} automatically reduces gap by one step (xllg). Depth 2+ reduces by two steps (floor: xs).

You can pass depth manually to skip the auto-reduction:

<Grid cols={3} gap="md" depth={0}> {/* stays "md" regardless */}</Grid>

7. Semantically correct lists

<Grid as="ul" cols={3} gap="md">
  {
    items.map((i) => (
      <GridItem as="li">
        <Card data={i} />
      </GridItem>
    ))
  }
</Grid>

8. Asymmetric sidebar layout

<Grid cols={12} gap="lg" align="start">
  <GridItem colSpan={3}>
    <Sidebar />
  </GridItem>
  <GridItem colSpan={9}>
    <MainContent />
  </GridItem>
</Grid>

9. In [slug].astro — replacing ContentList sections

{
  hasFeaturedContent && (
    <section class="container mx-auto px-4 py-12">
      <h2 class="mb-6 text-2xl font-bold">Featured</h2>
      <Grid cols="auto" minColWidth="320px" gap="lg" align="start">
        {[...projects, ...posts].map((e) => (
          <ContentCard entry={e} urlMap={urlMap} />
        ))}
      </Grid>
    </section>
  )
}

Depth and the CSS custom property

Every Grid writes --grid-depth as an inline CSS custom property and data-grid-depth as a data attribute. Use these for depth-aware styling without JS:

/* In your global CSS */
[data-grid-depth="0"] > * {
  /* root grid cells */
  border-radius: 1rem;
}
[data-grid-depth="1"] > * {
  /* nested grid cells */
  border-radius: 0.5rem;
}

/* Or via CSS custom property */
.my-card {
  border-radius: calc(1rem - var(--grid-depth, 0) * 0.25rem);
}

Tailwind v4 notes

  • Arbitrary column templates work: grid-cols-[repeat(auto-fill,minmax(280px,1fr))]
  • All class lookups use string literals — no safelist needed since classes are hardcoded in the component (not dynamically constructed with string interpolation)
  • twMerge handles conflicts when consumer passes class overrides
  • data-grid-depth and --grid-depth survive Astro’s scoped style boundary since they’re inline attributes/styles

Caveats

Masonry column count: cols="masonry" defaults to a responsive columns-1 sm:columns-2 lg:columns-3. Override with class:

<Grid cols="masonry" class="columns-2 md:columns-4" />

Depth prop is manual: Astro cannot automatically pass props through arbitrary slot children. Increment depth by hand when nesting:

<Grid cols={2} depth={0}>
  <Grid cols={3} depth={1}>
    ← you write depth={1}
    <Grid cols={2} depth={2}> ← and depth={2}</Grid></Grid
  ></Grid
>

gapX/gapY disable auto-reduction: If you set either explicitly, depth-aware gap reduction does not apply (your explicit value wins).

Latest Updates

Nothing here yet

Check back later or create some content!

Related Architecture

Context Links