position: sticky Not Working? 8 Causes and How to Fix Each
position: sticky not working? Check these 8 causes in order: missing top offset, overflow on a parent, short parents, flex/grid stretch, z-index, tables and Safari.
8 min read - Updated 2026-10-20
When position: sticky is not working, nothing throws an error. The element just scrolls away as if sticky were never declared. It almost always comes down to one of eight causes. Work down this list in order (the first two account for most cases) and you will find the culprit fast.
1. You forgot the threshold offset
This is the number one cause. Sticky needs at least one of top, right, bottom or left to know where to pin. Without it the declaration is valid but never engages.
.sidebar {
position: sticky;
top: 0; /* without an offset, sticky never engages */
}Add top: 0 (or another edge) to the sticky element itself
The offset is measured from the scroll container's edge, not the viewport
Under a fixed header? Use the header's height as the top value
Check DevTools' Computed panel to confirm top isn't overridden to auto
2. A parent has overflow hidden, auto or scroll
If any ancestor between the sticky element and the page has overflow set to hidden, auto or scroll, it becomes the scroll container and the element sticks to that box instead, usually invisibly. The classic culprit is overflow-x: hidden on a page wrapper, added to hide a horizontal scrollbar. Paste this into the DevTools console to find every offending ancestor.
let el = document.querySelector(".sticky");
while ((el = el.parentElement)) {
const { overflowX, overflowY } = getComputedStyle(el);
const ok = (v) => v === "visible" || v === "clip";
if (!ok(overflowX) || !ok(overflowY)) console.log(el, overflowX, overflowY);
}Run the snippet, replacing .sticky with your element's selector
Replace overflow-x: hidden with overflow-x: clip: it clips without creating a scroll container
Or remove the overflow and fix whatever was overflowing
If the ancestor must scroll, that's fine: the element will stick inside it
3. The parent is too short
Sticky is constrained to its direct parent. If the parent is only as tall as the sticky element, there is no room to travel, so it looks like it isn't sticking at all. If it sticks for a while and then scrolls away halfway down the page, that is the parent ending.
Give the parent enough height for scrolling to happen inside it
Move the sticky element up the tree to a taller ancestor
Watch for wrappers that only exist for styling and shrink the container
Outline the parent in DevTools to see exactly where it ends
4. It's a grid or flex item that stretches
Grid and flex items stretch to fill the row by default (align-items: stretch). A sticky sidebar inside display: flex or display: grid is therefore already as tall as its container and has nowhere to move.
.layout { display: flex; gap: 2rem; }
.sidebar {
position: sticky;
top: 80px;
align-self: flex-start; /* stop the stretch so it can travel */
}Add align-self: start to the sticky item
Or set align-items: start (flex-start) on the container
Confirm in DevTools that the item's height now matches its content
Long sidebars: add max-height and overflow-y: auto to the item itself
5. It is sticking, but something covers it
Sometimes sticky works and you just can't see it: later elements with position, transform or opacity paint on top, or the sticky element has a transparent background so content shows through.
Give the sticky element a background color
Add a z-index higher than the content that scrolls under it
Check for a new stacking context on the parent (transform, filter, opacity < 1)
Temporarily add outline: 2px solid red to see where it really is
6. The offset fights another element
A sticky element with top: 0 under a fixed header will stick behind that header. Two sticky elements with the same offset replace each other instead of stacking.
Set top to the fixed header's height (a CSS variable keeps them in sync)
Stack multiple sticky bars by giving each a larger top value
Recalculate offsets at breakpoints where the header height changes
Use scroll-margin-top so anchor jumps don't land under the sticky bar
7. Tables: sticky on the wrong element
For sticky table headers, put position: sticky on the th cells (thead th) rather than the table. With border-collapse: collapse, borders on sticky cells don't travel with them, so the header looks broken even though it sticks.
Target thead th { position: sticky; top: 0; }
Give th a background so rows don't show through
Use border-collapse: separate, or draw the border with box-shadow
For a scrolling table, put overflow: auto on a wrapper with a max-height
8. Old Safari or a width-specific bug
Every current browser supports position: sticky unprefixed. Safari versions before 13 needed position: -webkit-sticky. More often the bug is width-specific: a media query adds overflow, changes the layout to flex, or the header is taller than a landscape phone screen.
Add position: -webkit-sticky before position: sticky only if you still support very old iOS
Search your media queries for overflow and display changes
Test landscape phones, where sticky headers eat most of the viewport
Spot-check real Safari after fixing it in Chromium
Catch sticky bugs at every breakpoint in one scroll
Sticky bugs hide at specific widths, so checking one viewport at a time is how they reach production. Sizzy shows your localhost on phones, tablets and desktops side by side with scrolling synced: scroll once and you see every sticky header and sidebar engage and release on every device, then inspect any one of them with Chromium DevTools.
Synced scrolling across every device in one window
Full Chromium DevTools for each device
Landscape and custom device sizes for the short-screen cases
Framed screenshots to attach to the bug ticket
Release checklist
Threshold offset (top/bottom/left/right) is set on the sticky element.
No ancestor has overflow hidden/auto/scroll (use overflow: clip instead).
The parent container is tall enough to allow travel.
Grid/flex sticky items have align-self: start.
The sticky element has a background and a z-index.
Behavior verified at multiple viewport widths and in Safari.
Frequently asked questions
Why is position sticky not working at all?
In most cases you are missing a threshold offset: sticky does nothing without top, bottom, left or right. The next most likely cause is an ancestor with overflow: hidden, auto or scroll capturing the scroll context.
Does overflow: hidden break position sticky?
Yes, if it is on an ancestor between the sticky element and the page. That ancestor becomes the scroll container, and the element only sticks within it, which usually looks like it isn't sticking at all. Use overflow: clip instead, which clips without creating a scroll container.
Why is position sticky not working in a flex or grid container?
Flex and grid items stretch to the full height of the container by default, so the sticky item has no room to move. Add align-self: start (or flex-start) to the sticky item.
Why does sticky stop halfway down the page?
Sticky is constrained to its direct parent. Once the parent's bottom edge reaches the element, it un-sticks. If it stops earlier than expected, the parent is shorter than you think: give it more height or move the element up the tree.
Why is my sticky header not working on mobile?
Usually a media query adds overflow to a wrapper or switches the layout to flex at that width. Check the mobile styles for overflow and display changes, and test in a real mobile Safari as well as Chromium.
Do I still need position: -webkit-sticky?
Only for Safari versions before 13. Every current browser supports position: sticky without a prefix.
Sources and further reading
Related guides
Benji
Your life OS
The companion app that keeps every area of your world in sync.
Zero to Shipped
Ship products, not side projects
The ultimate Next.js boilerplate for building and launching real products.
DMX
Mindful Twitter/X
The intentional X client for macOS. Reclaim your attention span.
Sotto
Voice-to-text for macOS
Speak naturally. Type instantly. 100% local & private.
Passlock
Password manager with willpower
Lock passwords with time delays, word challenges, or hand the keys to someone you trust.
Glink
Changelogs that slap
Beautiful changelogs and roadmaps for your product.
Perkz
GitHub access control
Sell access to private GitHub repositories.
Tubely
YouTube Studio for Mac
Manage multiple YouTube channels in one native app.
JustWrite
Distraction-free writing
A minimal writing app that helps you focus on what matters.