On almost every website I build, there’s some kind of card grid where the elements share the same height. Everything works fine until the client wants different amounts of text in each card.
One fix is limiting the number of characters displayed, as in this example:
Lorem Ipsum dolor sit amet, consectetur adipiscing elit. Aliquam varius semper odio, id ornare est facilisis at. Nullam ac libero nec dui dignissim malesuada. Nullam eu ante vel est convallis dignissim. Fusce suscipit, ante a sagittis mattis.
ButtonLorem Ipsum dolor sit amet, consectetur adipiscing elit. Nullam ac libero nec dui dignissim malesuada. Nullam eu ante vel est convallis dignissim. Fusce suscipit, ante a sagittis mattis.
ButtonWhat’s the problem with this?
This approach is quite limiting. The moment the client adds an image to one card or writes more text in another, the buttons at the bottom stop aligning – even though Flexbox keeps the card heights equal:
Lorem Ipsum dolor sit amet, consectetur adipiscing elit. Nullam ac libero nec dui dignissim malesuada. Nullam eu ante vel est convallis dignissim. Fusce suscipit, ante a sagittis mattis.
Button
Lorem Ipsum dolor sit amet, consectetur adipiscing elit. Nullam ac libero nec dui dignissim malesuada. Nullam eu ante vel est convallis dignissim. Fusce suscipit, ante a sagittis mattis.
ButtonFlexbox gives us a clean solution. The client can insert any content they want, and the button stays pinned to the bottom.
Flex containers calculate the available space within them, giving us much greater flexibility. Using the flex-grow property, we can achieve this:
Lorem Ipsum dolor sit amet, consectetur adipiscing elit. Nullam ac libero nec dui dignissim malesuada. Nullam eu ante vel est convallis dignissim. Fusce suscipit, ante a sagittis mattis.
Button
Lorem Ipsum dolor sit amet, consectetur adipiscing elit. Nullam ac libero nec dui dignissim malesuada. Nullam eu ante vel est convallis dignissim. Fusce suscipit, ante a sagittis mattis.
ButtonThe button stays attached at the bottom regardless of content length. I’ve seen many developers reach for outdated techniques like absolute positioning or
min-height hacks, but flex-grow solves this cleanly.You can also achieve this with margin-top: auto on the button. Both approaches work – margin-top: auto pushes the element down by consuming available space above it, while flex-grow on the preceding element expands it to fill the gap. I’ll show the flex-grow approach here since it’s more explicit about what’s happening.
The Solution – Using flex-grow
Let’s walk through the solution step by step.
Step 1 – Writing the Markup
A basic container holds several cards. Each card contains a title, optional image, some text, and a button.
<div class="flex-alignment">
<div>
<h2>Title</h2>
<p>Lorem Ipsum dolor sit amet, consectetur adipiscing elit. Aliquam varius semper odio, id ornare est facilisis at. Nullam ac libero nec dui dignissim malesuada.</p>
<a href="">Button</a>
</div>
<div>
<h2>Title</h2>
<p>Lorem Ipsum dolor sit amet, consectetur adipiscing elit. Nullam ac libero nec dui dignissim malesuada.</p>
<a href="">Button</a>
</div>
</div>Step 2 – Setting It as a Flex Container
As I explained in my Flexbox guide, adding display: flex to the container is all it takes. This lets us control the child elements inside it.
.flex-alignment {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}The “children” here are the two
<div>elements inside the container with the class.flex-alignment.
Step 3 – Making Each Card a Flex Container Too
Adding display: flex and flex-direction: column to each card makes them stretch vertically to equal height in each row. Now we can control the elements inside each card – the title, the image, the text, and the button:
.flex-alignment > div {
display: flex;
width: calc(50% - 10px);
flex-direction: column;
padding: 15px;
box-sizing: border-box;
}The
widthvalue changes based on how many cards you want per row. For a flexible approach, check out my post on creating a responsive grid layout without media queries.
Step 4 – Pinning the Button to the Bottom
The flex-grow property defines how much a flex item should grow relative to its siblings within the same flex container:
.flex-alignment p {
flex-grow: 1;
}We set flex-grow: 1 on the <p> element that holds the card’s text. This tells it to expand and fill all remaining space.
Since we’re using flex-direction: column, the paragraph grows vertically – pushing the button below it to the bottom of the card.
The margin-top: auto Alternative
There’s an even simpler way to achieve the same result. Instead of applying flex-grow to the paragraph, apply margin-top: auto directly to the button:
.flex-alignment a {
margin-top: auto;
}This works because auto margins in a flex container absorb all available free space. The button’s top margin expands to fill the gap, pushing the button to the bottom.
Both methods produce the same visual result. I prefer flex-grow when I want the text area to visually fill the card, and margin-top: auto when I just need the button pinned down with minimal CSS.
FAQs
Common questions about pinning elements to the bottom of a flex container:
flex-grow: 1 tells the flex item to absorb all remaining free space inside its flex container. When used with flex-direction: column, the element expands vertically, pushing sibling elements below it to the bottom of the container.margin-top: auto is simpler - one line on the button. flex-grow: 1 on the text element is more explicit and also makes the text area visually fill the card. Choose whichever fits your use case.flex-direction: row). To pin an element to the bottom you need the items stacked vertically, so flex-direction: column is required on the card container.padding-bottom to the card to prevent the button from overlapping content. With flex-grow or margin-top: auto, the layout adjusts naturally - no magic numbers needed.grid-template-rows to define row sizes, or apply align-self: end on the button. However, for simple card layouts where you just need one element pinned to the bottom, Flexbox with flex-grow or margin-top: auto is usually simpler.width property on each card (for example, calc(33.33% - 10px) for three per row). The flex-grow pinning technique works the same regardless of how many cards appear in each row.Summary
The flex-grow property is one of the most practical Flexbox features for real-world layouts. By applying flex-grow: 1 to the content area of a card, you push the button (or any trailing element) to the bottom – no absolute positioning, no fixed heights.
If you want the simplest possible solution, margin-top: auto on the trailing element does the same job in a single line.

