Skip to content
Carl Victor Fontanos
Carl Victor Fontanos

Carl Victor Fontanos

Software Engineer

I build web applications and share what I learn along the way.

© 2026

position: sticky Isn't Broken - Your Overflow Is

C
Carlo Fontanos
· 2 min read

position: sticky has a reputation for "randomly not working" that it doesn't deserve. In years of fixing other people's sticky bugs, the cause has been the same two things at least nine times out of ten.

Culprit #1: an ancestor with overflow

A sticky element sticks relative to the nearest scrolling ancestor. Any ancestor with overflow: hidden, auto, scroll - or even clip in older engines - hijacks or kills the effect, because your element now sticks within that box instead of the page. And overflow: hidden gets sprinkled everywhere: clearfix leftovers, "fix horizontal scrollbar" hacks, carousel wrappers.

Paste this in the console; it walks the tree and names the saboteur:

let el = $0;   // select the sticky element in the Elements panel first
while ((el = el.parentElement)) {
    const { overflow, overflowX, overflowY } = getComputedStyle(el);
    if (/(auto|scroll|hidden|clip)/.test(overflow + overflowX + overflowY)) {
        console.log('offending ancestor:', el, overflow, overflowX, overflowY);
    }
}

Fixes, in order of preference: remove the overflow if it was cargo-culted; replace overflow-x: hidden on body-level wrappers with overflow-x: clip (clip doesn't create a scroll container in modern browsers, so sticky keeps working); or restructure so the sticky element isn't inside the clipped subtree.

Culprit #2: the parent is exactly as tall as the sticky element

A sticky element can only travel within its parent's box. If the parent is a flex/grid item that's only as tall as the sticky content itself, there's no room to move - it "sticks" by standing perfectly still.

/* The classic sticky-sidebar fix: stop the flex item from stretching */
aside {
    position: sticky;
    top: 24px;
    align-self: start;   /* THIS - flex/grid children default to stretch */
}

align-self: start is the single most useful line in this article. Grid and flexbox stretch children to equal height by default, which gives the sidebar zero slack. Start-aligning restores the travel room.

The two-second checks

  • Did you set an inset? position: sticky without top/bottom/left/right does nothing. top: 0 is not implied.
  • thead/tr can't stick in some engines - put sticky on the th/td cells instead.
  • Sticky headers overlapping anchored content afterwards is a different bug with its own one-line fix.

Once you internalize "sticky = constrained by nearest scroll container and parent height", the randomness evaporates. It's one of the best-behaved layout features we have - it just has two hard rules nobody tells you about.

C
Written by Carlo Fontanos

Full-stack web developer sharing practical tutorials and building tools that ship.

Got something on your mind?

My inbox is open - no forms disappearing into the void here.

  • Just say hello Found a tutorial useful? Spotted a mistake? Tell me.
  • Hire me for a project Have something custom in mind? Let's talk scope and timelines.
  • Product support Bought something here? I'll help you get it running.

I usually reply within 1-2 business days.

Message sent!

Your details are only used to reply to you.

Keep reading