Browse by section

Web Design 日本語

CSS Footer at the Bottom: Sticky vs Fixed Explained

A page with very little content often leaves the footer floating halfway up the screen. This article covers how to fix that with CSS.

The short answer: “pinning the footer to the bottom” means two different things, and they need completely different CSS. To stop the footer floating when content is short, use flex or grid. To keep the footer visible while scrolling, use position: fixed.

The two get confused constantly. The first is a sticky footer, the second is a fixed footer. This article covers both, plus the traps in position: absolute and position: sticky.

Sponsored

Which problem are you actually solving?

The short answer: the CSS depends on whether you want to stop the float or keep the footer on screen.

Name What you want CSS to use
Sticky footer Footer sits at the bottom of the viewport when content is short, and at the end of the document when it is long flex or grid
Fixed footer Footer stays pinned to the bottom of the screen while scrolling position: fixed

A confirmation page with three lines of text, where the footer lands awkwardly in the middle, is the sticky footer case. A persistent cart total bar is the fixed footer case.

Sticky footer with Flexbox

The short answer: make body a vertical flex container at least as tall as the viewport, and let the element above the footer grow.

<body>
  <div class="wrapper">
    <!-- page content -->
  </div>
  <footer>Footer content</footer>
</body>
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  margin: 0;
}

.wrapper {
  flex: 1;
}

flex: 1 makes .wrapper absorb all the leftover height, so with short content it expands and pushes the footer down. With long content it simply grows and the footer ends up after it.

Use min-height: 100vh, not height: 100vh. A fixed height overflows as soon as the content is taller.

This is the first thing to reach for. You never need to know the footer’s height, and it does not break when that height changes.

Sponsored

The same thing with CSS Grid

The short answer: grid-template-rows: 1fr auto on body gives the identical result.

body {
  display: grid;
  grid-template-rows: 1fr auto;
  min-height: 100vh;
  margin: 0;
}

1fr is the content area and auto is the footer. The footer takes exactly its own height and the rest goes to 1fr.

Unlike the Flexbox version, you write nothing on the child elements. Add a header with grid-template-rows: auto 1fr auto.

For more on this kind of layout, see the 2024 CSS Grid updates and how to use them.

Fixed footer that stays on screen

The short answer: use position: fixed with bottom: 0.

footer {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 60px;
}

body {
  padding-bottom: 60px; /* room for the footer */
}

A fixed element leaves normal flow, so without help the end of your content hides underneath it. Give body a padding-bottom equal to the footer height. Forgetting this is the most common mistake here.

If matching the number by hand is tedious, hold the height in a custom property.

:root {
  --footer-height: 60px;
}

footer {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  height: var(--footer-height);
}

body {
  padding-bottom: var(--footer-height);
}

On phones the screen is short, so a permanently visible footer eats into the readable area. Releasing the fixed positioning at small widths with a media query is a legitimate choice.

Sponsored

What about position absolute and position sticky?

The short answer: both can put a footer at the bottom, and both carry conditions that make them fragile. Reach for Flexbox, Grid or position: fixed first.

absolute needs a parent with a known height

The short answer: position: absolute; bottom: 0; on its own does nothing. The containing block needs position: relative, and its height has to be forced to fill the viewport.

body {
  position: relative;
  min-height: 100vh;
  padding-bottom: 60px; /* footer height */
  margin: 0;
  box-sizing: border-box;
}

footer {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 60px;
}

All of that to match what Flexbox does in a few lines. The footer height appears in two places, on the footer and in padding-bottom, so changing it breaks the layout.

position sticky does not make a sticky footer

The short answer: despite the name, position: sticky does not solve the sticky footer problem.

A footer with position: sticky; bottom: 0; clings to the bottom of the viewport while you scroll and settles into its normal position once you reach the end of the document. That behaviour is closer to fixed.

footer {
  position: sticky;
  bottom: 0;
}

It will not fix a footer floating on a short page. For that, use Flexbox or Grid.

Use 100dvh when 100vh misbehaves on mobile

The short answer: iOS Safari calculates 100vh against the height with the address bar hidden, so the footer sits off screen while the bar is showing. 100dvh uses the height actually in view.

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;  /* fallback for older browsers */
  min-height: 100dvh; /* used where supported */
  margin: 0;
}

dvh is the dynamic viewport unit, supported from Chrome 108, Safari 15.4 and Firefox 101. Write the 100vh line first, and browsers that do not understand dvh ignore the second line and keep the vh value.

Write the footer as a footer element

The short answer: use <footer> for the page footer rather than <div class="footer">.

<footer> is defined as the element that groups a section’s copyright, related links and contact details. Screen readers expose it as a landmark, which helps assistive technology users move around the page.

A <footer> inside <article> or <section> belongs to that block instead. For the page footer, place it as a direct child of <body>.

If a footer element also needs to show and hide, choosing between display, visibility and opacity covers which property to use.