How to Vertically Align Elements With CSS Subgrid
Use CSS row subgrid to align repeated component rows, and learn when to use align-items, align-self, or align-content instead.
To line up corresponding content rows across repeated components, define the rows on a parent CSS grid and make each child component span those rows with grid-row and grid-template-rows: subgrid. Subgrid shares the parent’s track geometry; it does not center items inside their grid areas. For that, use align-items or align-self.
Align matching rows across cards
This pattern is useful when cards have different title or description lengths but their action links should occupy a consistent row. The parent establishes three row tracks; each card spans them and adopts them as its own rows.
<div class="cards">
<article class="card">
<h2>Card title</h2>
<p>Card descriptions can have different lengths.</p>
<a href="#details-one">Read more</a>
</article>
<article class="card">
<h2>Another title</h2>
<p>A shorter description.</p>
<a href="#details-two">Read more</a>
</article>
</div>
.cards {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-rows: auto 1fr auto;
gap: 1rem;
}
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid;
}
Each card’s first, second, and third children participate in the corresponding shared rows: title, description, and action. The content and track sizing determine the actual heights; this does not promise fixed-height cards. The 1fr middle track takes available space according to the parent grid’s sizing context, while the auto tracks account for their contents.
Subgrid is a value for the dimension you want to share. Use grid-template-rows: subgrid to share rows, grid-template-columns: subgrid to share columns, or both if both dimensions need the parent tracks. The nested component must itself be a grid item of the parent, establish a grid with display: grid, and span the parent tracks it needs. MDN explains that a subgrid uses tracks defined on its parent rather than creating a separate track listing in that dimension in its Subgrid – CSS guide.
Shared tracks versus centering items
“Vertically align” can mean two different layout jobs. In the common horizontal writing mode, the block axis is vertical, so these properties affect that direction. CSS grid’s axes are logical, though: in another writing mode, the block axis may not correspond to the physical vertical direction.
| Goal | Use | What it affects |
|---|---|---|
| Make corresponding rows in sibling components share sizing and position | grid-template-rows: subgrid, with a suitable row span |
Track geometry inherited from the parent |
| Align each item within its own grid area on the block axis | align-items on the grid, or align-self on one item |
Items inside their grid areas |
| Position the grid’s tracks together when the container has extra space | align-content |
The tracks as a group inside the grid container |
For example, to center each card’s contents inside their areas, add align-items: center to the card. To center only one item, use align-self: center on that item. These rules do not create shared tracks; use subgrid when matching track sizes across nested siblings is the goal.
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid;
align-items: center;
}
.card a {
align-self: center;
}
Use align-content only when the grid has free space and the tracks as a group need positioning within the grid container. It does not determine which parent rows a nested component spans and is not a replacement for subgrid or item alignment.
Choose rows, spans, gaps, and sizing
- List the repeated content roles. In the example, each card has a title, a description, and a link. The cards need matching rows in that order.
- Define the parent tracks. Give the parent enough rows for those roles and use track sizing that fits the design. Content-sized
autorows can grow to fit content. - Make each nested component span those tracks. The span must cover the rows its children should use. If components occupy more or fewer rows in a responsive layout, adjust the parent grid and component placement together.
- Enable subgrid in the needed direction. Add
display: gridandgrid-template-rows: subgridto the grid item. Add column subgrid separately if columns also need to align. - Add item alignment only if needed. Choose
align-itemsoralign-selffor placement within each area.
Subgrid inherits the parent’s gaps in the subgridded dimension. A subgrid may override its gap. Setting the subgrid gap to zero can give space back to an item in a way similar to a negative margin; it does not remove the parent’s gap for other grid items. Check the resulting spacing at the component boundaries as well as between the subgrid’s own items.
Content in a subgrid can contribute to parent track sizing when the sizing method allows those contributions. In particular, auto-sized rows can grow to fit content from the main grid and its subgrids. If you need a strict height, specify and manage track sizing deliberately; do not assume subgrid alone makes every row or card a fixed height.
Common approaches that solve a different problem
align-itemsalone: use it for aligning items within their grid areas. It does not make independently sized nested grids share row tracks.align-contentalone: use it to place a group of tracks when there is extra space. It does not align corresponding internal rows across sibling components.vertical-align: this applies to inline, inline-block, and table-cell boxes, not general block-level or grid items. See MDN’s vertical-align CSS property reference.- Independent nested grids: each grid sizes its own tracks, so varying content can cause corresponding rows to start or end at different positions. Use them when each component should size independently.
Browser support and graceful fallback
MDN describes subgrid as widely available across browsers and available across browsers since September 2023. If your project must support older or embedded browser engines, check the compatibility data for the exact browsers and versions your users need before relying on it.
A layout without subgrid can still be usable, but its nested rows will size independently. A basic fallback is to keep the parent and cards as grids and provide local rows, accepting that variable content may not line up perfectly:
.card {
display: grid;
grid-template-rows: auto 1fr auto;
}
@supports (grid-template-rows: subgrid) {
.card {
grid-row: span 3;
grid-template-rows: subgrid;
}
}
This fallback preserves a sensible card layout in browsers without subgrid, but it cannot guarantee matching internal row positions there. Confirm that the local track sizing suits your content and responsive layout.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
subgrid appears to do nothing |
The nested element is not a grid item of the intended parent, lacks display: grid, or does not span the parent tracks. |
Check the parent-child relationship, establish the nested grid, and set its row or column span to cover the tracks it should share. |
| Titles line up, but descriptions or links do not | Children are in a different order, there are more child rows than parent tracks, or the component span and row definitions do not match. | Map each child role to a parent row, then make the span and track count match that mapping. |
| Items share rows but are not centered | Subgrid shares track geometry; item alignment is a separate setting. | Set align-items on the subgrid or align-self on the specific item. |
| Cards become taller than expected | Content contributes to content-sized parent tracks, or a long title or description needs more room. | Review the parent’s track sizing and content. Use explicit sizing only when a constrained height is part of the design and overflow behavior is accounted for. |
| Spacing changes at the nested component boundary | The subgrid inherits parent gaps or overrides its own gap. | Inspect both parent and subgrid gap values. Adjust the subgrid gap intentionally and check how the recovered space affects its items. |
| A supposed vertical alignment rule works in an unexpected direction | Grid alignment properties use logical axes, which depend on writing mode. | Identify the block axis for the writing mode in use; do not assume it is physically vertical in every writing mode. |
Performance, reliability, and cost
CSS subgrid is a browser layout feature, so this pattern does not require a server request or a paid service. Keep the grid structure tied to the actual component roles, avoid imposing fixed heights without a content and overflow plan, and test layouts with short and long content at the breakpoints you support. For reliability, verify required browser support and keep a fallback if your audience includes engines without subgrid.
Or skip the browser setup
If you are documenting this layout or need a screenshot of an example page, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. The API also supports full-page capture and a custom viewport, which can help capture a grid example at the size you want. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. An MCP server lets AI agents use screenshot tools. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
FAQ
Can subgrid center a single item vertically?
Subgrid shares tracks. To center one item inside its grid area, use align-self: center in the common horizontal writing mode, or choose the alignment based on the logical block axis for other writing modes.
Can I subgrid columns and rows at the same time?
Yes. Specify subgrid on both grid-template-rows and grid-template-columns when the nested layout should use the parent tracks in both dimensions.
Does subgrid force every card to have the same height?
No. Shared tracks and content-based sizing determine the result. Content can affect auto-sized tracks, and explicit height constraints require their own sizing and overflow decisions.


