X Xerobit

CSS Sticky Footer with Flexbox — Keep Footer at Bottom of Page

A sticky footer stays at the bottom of the viewport when content is short, and below the content when content is tall. The flexbox approach is the cleanest modern solution...

Mian Ali Khalid · · 4 min read
Use the tool
Flexbox Generator
Visual CSS Flexbox playground. Live preview + copyable CSS. Control direction, wrap, justify, align, gap. Example child layouts.
Open Flexbox Generator →

A sticky footer stays at the bottom of the viewport when content is short, and scrolls naturally below the content when content is long. Flexbox makes this effortless.

Build flexbox layouts with the Flexbox Generator.

/* HTML structure:
   <body>
     <header>...</header>
     <main>...</main>
     <footer>...</footer>
   </body>
*/

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;    /* At least full viewport height */
  margin: 0;
}

main {
  flex: 1;              /* Grow to fill available space */
  /* This pushes the footer to the bottom */
}

footer {
  /* No special styling needed — footer stays at bottom automatically */
}

That’s it. When content is short, main { flex: 1 } expands to fill the space between header and footer. When content is tall, min-height: 100vh allows the page to grow naturally.

With multiple sections

body {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;   /* dvh = dynamic viewport height (mobile-safe) */
}

header { flex-shrink: 0; }
nav    { flex-shrink: 0; }

main {
  flex: 1;  /* Pushes everything below it to the bottom */
}

.pre-footer { flex-shrink: 0; }
footer      { flex-shrink: 0; }

Using 100dvh instead of 100vh

On mobile browsers, 100vh can include or exclude the browser’s dynamic toolbar (address bar), causing layout jumps. Use 100dvh for modern mobile-safe behavior:

body {
  min-height: 100dvh;  /* dvh = dynamic viewport height */
  /* 100dvh = visible viewport height, excludes mobile browser chrome */
}

/* Fallback for older browsers: */
body {
  min-height: 100vh;
  min-height: 100dvh;  /* Overrides if supported */
}
/* For an app with a fixed header, scrollable content, fixed footer */
.app-shell {
  display: flex;
  flex-direction: column;
  height: 100dvh;       /* Exact viewport height */
  overflow: hidden;
}

.app-header {
  flex-shrink: 0;
  height: 64px;
}

.app-main {
  flex: 1;
  overflow-y: auto;     /* Scroll only the main area */
  overflow-x: hidden;
}

.app-footer {
  flex-shrink: 0;
  height: 56px;
}

React layout component

function PageLayout({ children }) {
  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'column',
        minHeight: '100dvh',
      }}
    >
      <Header />
      <main style={{ flex: 1 }}>
        {children}
      </main>
      <Footer />
    </div>
  );
}

With Tailwind CSS

<body class="flex flex-col min-h-screen">
  <header class="shrink-0">...</header>
  <main class="flex-1">...</main>
  <footer class="shrink-0">...</footer>
</body>
/* A card with a footer button always at the bottom */
.card {
  display: flex;
  flex-direction: column;
  height: 400px;        /* Fixed height card */
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  padding: 1.5rem;
}

.card-content {
  flex: 1;              /* Takes all available space */
  overflow-y: auto;     /* Scroll if content is long */
}

.card-footer {
  padding-top: 1rem;
  border-top: 1px solid #e2e8f0;
  /* Always at bottom of card */
}

Comparison: old fixed vs flexbox approach

/* ❌ Old approach: margin trick */
.content { margin-bottom: 80px; }  /* Equal to footer height */
footer {
  position: fixed;
  bottom: 0;
  width: 100%;
  height: 80px;
}
/* Problems: fixed footer covers content, scroll behavior issues */

/* ❌ Old approach: table layout */
html, body { height: 100%; }
.wrapper { display: table; height: 100%; width: 100%; }
.main    { display: table-row; height: 100%; }
footer   { display: table-row; }
/* Works but semantically wrong and fragile */

/* ✅ Modern: flexbox */
body { display: flex; flex-direction: column; min-height: 100dvh; }
main { flex: 1; }

Related posts

Related tool

Flexbox Generator

Visual CSS Flexbox playground. Live preview + copyable CSS. Control direction, wrap, justify, align, gap. Example child layouts.

Written by Mian Ali Khalid. Part of the Frontend & Design pillar.