Sticky Footer with Flexbox
A footer that stays at the bottom of the viewport on short pages and at the bottom of the content on long pages used to require absolute positioning, fixed heights, or table-cell tricks. Flexbox solves it with two declarations. This snippet covers the canonical body-flex layout, the grid-based equivalent, and the wrapper variant for apps that already have a top-level container.
505 views
7
html, body {
margin: 0;
padding: 0;
height: 100%;
}
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex: 1;
}
footer {
background: hsl(220 15% 95%);
padding: 1rem;
}Setting body to display: flex; flex-direction: column; min-height: 100vh makes the body at least the viewport tall and stacks its children vertically. main gets flex: 1, which means it expands to consume any leftover space, pushing the footer to the bottom on short pages. On long pages the footer sits naturally below the content because flex: 1 does not force the main to be larger than its content. The min-height: 100vh (instead of height: 100vh) is what allows the page to grow past the viewport when content overflows.
body {
margin: 0;
min-height: 100vh;
display: grid;
grid-template-rows: auto 1fr auto;
}
header {
background: hsl(220 90% 56%);
color: white;
padding: 1rem;
}
main {
padding: 1rem;
}
footer {
background: hsl(220 15% 95%);
padding: 1rem;
}Grid expresses the same idea with one extra row: auto 1fr auto says the header is its content size, the main takes whatever is left, and the footer is its content size. This generalises better when the layout grows (sidebar plus footer, multi-row hero) because adding columns or rows is just appending a track to grid-template-*. The flex version still wins on simplicity for a header / main / footer page; switch to grid the moment a sidebar enters the picture.
.app {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.app__main {
flex: 1;
}
.app__footer {
background: hsl(220 15% 95%);
padding: 1rem;
}If the body cannot be the flex container (an embedded widget, a Next.js app where the body class is reserved), the same pattern works on a wrapper element. The only requirement is that the wrapper itself has a min-height of 100vh so it always fills the viewport. Any direct children inside .app__main get the natural full-height behaviour. Use this version inside a Next.js app/layout.tsx, a Storybook decorator, or any embedded widget that should still pin its footer.
