Position: sticky usually stops working when the sticky element has no offset value, a parent creates the wrong scroll container, or the surrounding layout gives the element no real space to stick.
CSS Sticky Fix
Fix position: sticky not working.
If your sticky header, sidebar, filter bar, table header, or navigation element refuses to stick, the problem is usually not the position:sticky declaration by itself. Sticky only works when the browser can calculate a scroll threshold, a containing area, and enough movement space. If one parent in the chain has the wrong overflow, height, alignment, or scroll behavior, the sticky element can act like normal position:relative.
- Sticky headers
- Sticky sidebars
- Overflow traps
- Scroll container bugs
What the bug looks like
The element scrolls away like normal content, sticks only for a tiny moment, stops too early, or behaves differently inside a sidebar, table, header, or dashboard layout.
Why it happens
Sticky is not just a property. It is a relationship between the sticky element, its offset, its ancestors, and the scroll container that controls the page movement.
What usually fixes it
Add a clear offset, remove accidental parent overflow, give the sticky element enough vertical space, and make sure the layout is not stretching the sticky column incorrectly.
The simple rule behind position:sticky
A sticky element behaves like a normal element until the page reaches a defined offset. Then it sticks inside the boundaries of its containing area. That means sticky needs two things at the same time: a normal layout position and a scroll threshold.
This is why position:sticky can feel confusing. It is not fully like fixed, because it does not attach to the viewport forever. It is not fully like relative, because it can temporarily stick during scroll. Sticky lives between normal flow and fixed positioning.
The sticky element has no offset value
This is the first thing to check. A sticky element needs a threshold such as top:0, top:20px, or sometimes bottom:0. Without that threshold, the browser has no clear point where sticky behavior should begin.
Broken code
No threshold.sidebar {
position: sticky;
}
Broken visual result
The element is sticky in name, but there is no offset telling the browser when to stick.
Correct code
Sticky threshold.sidebar {
position: sticky;
top: 24px;
}
Fixed visual result
The browser now knows the sticky element should stop when it reaches 24px from the top.
A parent has the wrong overflow
This is the sticky bug that wastes the most time. You add position:sticky and top:0, but it still does nothing. Then you inspect the parent chain and discover a wrapper with overflow:hidden, overflow:auto, or overflow:scroll.
Broken code
Overflow trap.page-shell {
overflow: hidden;
}
.sidebar {
position: sticky;
top: 24px;
}
Broken visual result
A parent wrapper is controlling overflow, so sticky may be trapped inside the wrong scroll context.
Correct code
Cleaner parent.page-shell {
overflow: visible;
}
.sidebar {
position: sticky;
top: 24px;
}
Fixed visual result
Once the accidental overflow rule is removed or moved to the right element, sticky has a cleaner scroll context.
The parent is too short for sticky to become visible
Sticky cannot stick forever. It only sticks inside the area of its parent or containing block. If the parent ends almost immediately, the sticky element has no useful distance to travel. This makes sticky look broken even when the CSS syntax is correct.
Broken code
Short parent.sidebar-wrap {
height: 120px;
}
.sidebar {
position: sticky;
top: 24px;
}
Broken visual result
The element may technically enter sticky mode, but there is not enough parent height to see it working.
Correct code
Enough space.layout {
display: grid;
grid-template-columns: 1fr 320px;
gap: 24px;
}
.sidebar {
position: sticky;
top: 24px;
align-self: start;
}
Fixed visual result
Sticky becomes easier to understand when the parent has enough height for the element to move and stop.
Grid or Flexbox is stretching the sticky element
Sticky sidebars often fail visually inside Grid or Flexbox because the browser stretches columns or items by default. The sticky element may become as tall as the row, or the sidebar column may not behave like a natural scroll companion. In many real layouts, align-self:start is the missing piece.
Broken code
Stretched item.layout {
display: grid;
grid-template-columns: 1fr 320px;
}
.sidebar {
position: sticky;
top: 24px;
}
Broken visual result
The sticky item is inside a layout context, but the alignment is not helping it behave like a compact sticky block.
Correct code
Start aligned.layout {
display: grid;
grid-template-columns: 1fr 320px;
gap: 24px;
align-items: start;
}
.sidebar {
position: sticky;
top: 24px;
align-self: start;
}
Fixed visual result
Start alignment prevents the sidebar from being stretched in a way that makes sticky behavior harder to reason about.
You actually need position:fixed, not sticky
Sometimes sticky is not the right tool. If you want the element to stay attached to the viewport all the time, even after its parent ends, sticky may disappoint you. Sticky is bounded by its container. Fixed positioning is viewport-based.
Wrong expectation
Container bound.floating-help {
position: sticky;
top: 24px;
}
Why this feels broken
Sticky only sticks inside its containing area. If the parent ends, the sticky behavior ends too. That is correct behavior, not a browser bug.
Use fixed when needed
Viewport bound.floating-help {
position: fixed;
right: 24px;
bottom: 24px;
}
When fixed is better
Use fixed for floating help buttons, persistent chat buttons, cookie banners, or elements that should stay attached to the viewport regardless of their parent.
Fast practical rule
If position:sticky is not working, do not start by adding random z-index values. Sticky failure is usually about offset, overflow, scroll context, parent height, or alignment. First check top. Then inspect every parent for overflow. Then check whether the sticky element has enough room to stick.
Sticky vs fixed: the key difference
position:sticky keeps the element in normal flow first, then lets it stick during scroll within a specific boundary. position:fixed removes the element from normal flow and attaches it to the viewport.
This difference matters because a fixed header can cover content if you do not reserve space for it, while a sticky header usually keeps its original space in the document flow.
Good sticky header pattern
Stable.site-header {
position: sticky;
top: 0;
z-index: 20;
background: white;
}
This pattern works well when the header should scroll normally at first and then stay visible at the top while the user continues down the page.
Sticky sidebar pattern
Production-minded.content-layout {
display: grid;
grid-template-columns: minmax(0, 1fr) 320px;
gap: 32px;
align-items: start;
}
.sidebar {
position: sticky;
top: 24px;
align-self: start;
}
Why this pattern is safer
The main content column can shrink safely because it uses minmax(0,1fr). The sidebar gets a fixed track, but it is aligned to the start instead of being stretched. The sticky element has a clear top threshold and a predictable layout context.
This is the kind of pattern that survives real pages better than a tiny demo that only works with perfect content.
Debug checklist
- Confirm the element has
position:sticky, not a typo likeposition: stickyedor a class that is not applied. - Add a clear offset such as
top:0,top:16px, ortop:24px. - Inspect every parent for
overflow:hidden,overflow:auto,overflow:scroll, or unusual shorthand overflow rules. - Check whether the sticky element is inside a parent that is too short for sticky behavior to become visible.
- If the sticky element is inside Grid or Flexbox, test
align-self:startoralign-items:start. - Check whether another wrapper is creating the real scroll container instead of the page.
- Do not use sticky when you really need viewport-level behavior. Use
position:fixedfor persistent floating UI. - Add a background and a useful
z-indexonly after sticky is actually working.
top and test again before changing anything else.
z-index when the real issue is parent overflow or missing offset.
Final takeaway
position:sticky fails when the browser cannot calculate a useful sticky relationship. The usual cause is not a mysterious browser bug. It is a missing offset, a parent with overflow, a container that is too short, or a layout context that stretches or traps the sticky element.
Start with the basics: add top, remove accidental overflow from parents, give the element enough scroll space, and align sticky sidebars to the start in Grid or Flexbox layouts. Once the surrounding structure is clean, sticky becomes predictable instead of frustrating.
Want more fixes like this?
Explore the full FrontFixer fixes library and keep debugging with practical guides built for real front-end layout problems.