Troubleshooting guide · html-css-js · Published 2026-08-16 · 3 min read
position sticky not working
Troubleshoot position sticky not working: overflow ancestors, height constraints, transform, and z-index in a checklist order.
- ·Define the symptom
- ·The constraint checklist
- ·Compound fixes
The two rules behind every sticky failure
Sticky positioning is a promise, not a behaviour. For an element to stick, two rules must hold:
- The element's nearest scrolling ancestor must actually be the element you want it to stick inside. An ancestor with
overflowon it creates a new scroll context, and the sticky element sticks to *that* context. - The element must have room to stick. Sticky only pins while the nearest scroll container has height left between the element's current offset and the scroll container's bottom edge.
Any failure is one of those two rules broken. Almost every real bug traces to an ancestor with overflow: hidden/auto/scroll or to a height constraint that leaves no travel distance.
Troubleshoot in this order
- Trust the symptom, then the scroll context. If the element never sticks at all, suspect an ancestor clipping scroll. Check the stack in DevTools: expand the element's parents and look for
overflow-x: hidden,overflow: auto, oroverflow-y: scrollon any ancestor.overflow-x: hiddenmakes the browser compute the box's overflow as hidden in both axes, which silently kills sticky (and contributes to the wider layout bugs covered in the layout techniques article). - Check the container has travel room.
position: sticky; top: 0cannot stick if the parent (or the element's own wrapper) hasheightthat equals the element height, or if the element is the last child and there is nothing to scroll past. Sticky needs a frame bigger than the sticky box: the box sticks inside its nearest block container, so the container must be taller than the box. - Watch the browser quirks. An ancestor with
transform,filter,perspective,contain: paint(or a historicalborder-radiusclipping bug) becomes the containing block, so the sticky element'stopstops meaning what you think. Remove them one at a time and verify the offset in DevTools after each change. - A sticky inside a sticky gets the inner one ignored by some browsers; keep one sticky per scroll interaction.
The three fixes that cover 90% of cases
| Cause | Fix |
|---|---|
An ancestor has overflow: hidden | Change that ancestor to overflow: visible in the axis, or move the sticky element outside it |
| The scroll parent is not the window | Set the sticky on the element whose parent is the element that scrolls, not on the window |
| No travel room | Give the container a taller sibling/content or wrap the sticky in a position: relative parent of full height |
A concrete example, the disappearing header:
html { overflow-x: hidden; } /* common pattern that breaks sticky */
header.sticky {
position: sticky;
top: 0;
}
Fix: use overflow-x: clip instead (which does not create a scroll container) or move the scroll restriction to a wrapper that does not contain the header.
When sticky is the wrong tool
If the panel must stay fixed regardless of the page (never scroll with content), position: fixed is the correct tool, with the touch that some fixed headers get a position: sticky-style rule instead to let the hero scroll under them. If the element must stick while within one section and float away after the next section starts, sticky is exactly right: the larger top-and-bottom thresholds (top: 0; bottom: 0 with a flex column) control that elegantly.
The layout techniques guide covers the ancestors that change scroll context, and the troubleshooting order article stacks the DevTools-first workflow this article assumes.