Layout Beginner

Sticky headers without JavaScript scroll listeners

The old way used JavaScript scroll events and getBoundingClientRect to toggle a class. Sticky positioning does it in one property.

3 lines
1.header {
2  position: sticky;
3  top: 0;
4}
Old 15+ lines
1// JavaScript: scroll listener
2window.addEventListener('scroll', () => {
3  const rect = header.getBoundingClientRect();
4  if (rect.top <= 0) header.classList.add('fixed');
5  else header.classList.remove('fixed');
6});
7
8.header.fixed {
9  position: fixed;
10  top: 0;
11}
Widely available Since 2022 96% global usage

This feature is well established and works across many devices and browser versions. It has been available across browsers since 2022.

56+
32+
13+
16+
position: sticky
πŸ“Œ I'm a sticky header
List item 1
List item 2
List item 3
List item 4
List item 5
List item 6
List item 7
List item 8
List item 9
List item 10
List item 11
List item 12
Kinsta

Your first month is free

Managed WordPress hosting for faster sites.

Learn more
⚑

No JavaScript

The browser handles scroll. No listeners, no getBoundingClientRect, no class toggles.

✦

Respects flow

Sticky stays in layout until it hits the threshold, then sticks. No layout jumps.

∞

One property

Set position and top. Works for headers, sidebars, or any element you want to pin.

Lines Saved
15+ β†’ 3
No JS needed
Old Approach
Scroll listener + class
JS + getBoundingClientRect
Modern Approach
One property
position: sticky

How it works

The old way meant a scroll listener, reading getBoundingClientRect on every scroll, and toggling a class to switch between normal and fixed. That's JS, reflows, and extra CSS for the fixed state.

With position: sticky and top: 0, the header stays in flow until it would scroll past the top, then it sticks. The browser does the work. No script, no class, no layout hacks.

New CSS drops.

Join 600+ readers who've survived clearfix hacks.

ESC