Why Is My Sticky Header Not Staying on Top?

Sticky header not staying on top problems usually happen when the header is missing a top value, trapped inside the wrong scroll container, placed behind content by z-index, or affected by overflow and stacking context rules.

Sticky Header Fix

Why is my sticky header not staying on top?

A sticky header can work for a few pixels, then disappear. It can stick inside the wrong parent, slide behind a hero section, or stop staying on top when another element creates a new stacking context. The fix is not just “add z-index.” You need to check the sticky offset, parent overflow, scroll container, and stacking order together.

  • position sticky
  • z-index
  • overflow hidden
  • stacking context

What the bug looks like

The header scrolls away, sticks only inside one section, sits behind content, or stops being clickable on mobile.

Why it happens

Sticky behavior depends on the nearest scroll container, the sticky offset, and the stacking context around the header.

What usually fixes it

Put the header high in the HTML, add a real top value, avoid trapping parent overflow, and use a deliberate z-index.

Error 1

position:sticky is missing the sticky offset

position:sticky does not mean “always stay at the top.” It means the element behaves normally until it reaches a specified offset. Without top:0, the browser may have no useful sticky boundary.

Broken code

Missing top value
.site-header {
  position: sticky;
  z-index: 10;
}

Broken visual result

Header scrolls away
not sticking
Brand
Hero section
Content keeps scrolling
The header has sticky positioning, but no clear top point where it should stick.

Correct code

Sticky offset added
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
}

Fixed visual result

Header knows where to stick
Brand
Hero section
Content scrolls under the header
top:0 tells the browser the exact point where the header should become sticky.
Error 2

A parent with overflow is trapping the sticky header

Sticky elements stick relative to their nearest scrolling ancestor. If the header is inside a wrapper with overflow:hidden, overflow:auto, or a short height, the header may only stick inside that wrapper instead of the page.

Broken code

Overflow trap
.page-shell {
  height: 320px;
  overflow: hidden;
}

.site-header {
  position: sticky;
  top: 0;
}

Broken visual result

Sticky is trapped
parent overflow
Brand
Wrapper area
The header cannot stick beyond this parent
The sticky header is not broken. It is obeying the wrong container.

Correct code

Header outside overflow wrapper
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
}

.page-shell {
  overflow: visible;
}

Fixed visual result

Sticky follows the page
Brand
Normal page content
No overflow trap
Keep the sticky header outside unnecessary overflow wrappers when it must stick for the whole page.
Error 3

The header is sticky, but it is behind other content

A header can stay at the top and still look broken if a hero, card, dropdown, or transformed section paints above it. In that case, the issue is stacking order, not sticky behavior.

Broken code

Low z-index
.site-header {
  position: sticky;
  top: 0;
  z-index: 1;
}

.hero-card {
  position: relative;
  z-index: 10;
}

Broken visual result

Content covers header
behind content
Brand
Hero card

This card paints above the sticky header because its z-index wins.

More page content
The sticky header is there, but another layer is visually covering it.

Correct code

Deliberate header layer
.site-header {
  position: sticky;
  top: 0;
  z-index: 1000;
}

.hero-card {
  position: relative;
  z-index: 1;
}

Fixed visual result

Header stays above content
above content
Brand
Hero card

The content keeps its layer, but the header has the higher page-level layer.

More page content
Use a consistent z-index scale for headers, menus, overlays, and content cards.
Error 4

The sticky header is inside the wrong HTML structure

A site-wide sticky header should usually live near the top of the page structure, not inside a hero section, card, slider, or small wrapper. If its parent ends, the sticky behavior ends with it.

Broken code

Header nested too deep
<section class="hero">
  <header class="site-header">...</header>
  <div class="hero-content">...</div>
</section>

<main>...</main>

Broken visual result

Sticky ends with section
wrong parent
Brand
Hero wrapper
Main content after hero
The header cannot stay sticky for the full page if it belongs to a short section.

Correct code

Header at page level
<header class="site-header">...</header>

<main>
  <section class="hero">...</section>
  <section class="content">...</section>
</main>

Fixed visual result

Header controls the page
Brand
Hero section
Content section
A page-level header has a page-level sticky boundary.
Premium pattern

A production-minded sticky header pattern

A reliable sticky header pattern uses a page-level header, a clear sticky offset, a deliberate z-index layer, and avoids placing the header inside wrappers that create scroll or stacking traps.

Premium code

Sticky header system
<header class="site-header">
  <a class="logo" href="/">Brand</a>
  <nav class="main-nav">...</nav>
</header>

<main class="site-main">
  ...
</main>
:root {
  --header-layer: 1000;
}

.site-header {
  position: sticky;
  top: 0;
  z-index: var(--header-layer);
  background: rgba(255,255,255,.96);
  border-bottom: 1px solid #e5e7eb;
}

.site-main {
  min-width: 0;
}

html {
  scroll-padding-top: 80px;
}

Premium visual result

Predictable sticky layer
Brand
Hero section
Content section
Premium sticky headers are not magical. They are page-level elements with clean parents and predictable layers.

Fast practical rule

If a sticky header is not staying on top, do not start by throwing bigger z-index values at it. First confirm top:0, then check parent overflow, then check whether another element created a stacking context above the header.

Debug checklist

  • Check that the header has position:sticky and a real offset like top:0.
  • Inspect every parent of the header for overflow:hidden, overflow:auto, or overflow:scroll.
  • Move the header outside short wrappers, hero sections, sliders, and cards when it should stick for the whole page.
  • Give the header a deliberate page-level z-index, such as z-index:1000.
  • Check whether another element has a higher z-index than the header.
  • Look for stacking context creators like transform, filter, opacity, isolation, or positioned parents.
  • Test mobile breakpoints to make sure the header is not switched back to position:static.
  • If anchor links hide under the header, add scroll-padding-top or scroll-margin-top.
Best first moveAdd or confirm top:0. Sticky needs an offset to know when to stick.
Most common causeA parent wrapper with overflow changes the sticky boundary.
Most misleading causeThe header is sticky, but content paints above it because of z-index or stacking context.
Better mindsetSticky headers need structure, not random z-index numbers.

Final takeaway

A sticky header not staying on top is usually not one single CSS mistake. It is usually a combination of sticky offset, parent overflow, z-index, stacking context, and HTML structure.

Start with top:0, then inspect the parent containers, then fix the stacking order. Once the header is a clean page-level element with a deliberate layer, it becomes predictable on desktop and mobile.

Want more fixes like this?

Browse more CSS, header, and responsive debugging guides in the FrontFixer library.

Fix position: sticky not working

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.

Error 1

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

It scrolls away
Sidebar / filter bar

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

It has a point to stick
Sticky sidebar / filter bar

The browser now knows the sticky element should stop when it reaches 24px from the top.

Error 2

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

Wrong scroll container
Sticky element

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

Parent no longer traps it
Sticky element

Once the accidental overflow rule is removed or moved to the right element, sticky has a cleaner scroll context.

Error 3

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

No room to stick
Sticky sidebar
The parent ends too soon, so the sticky behavior has almost no visible runway.

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

Enough scroll area
Sticky sidebar
The sticky element now has a meaningful containing area during scroll.

Sticky becomes easier to understand when the parent has enough height for the element to move and stop.

Error 4

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

Layout fights sticky
Sidebar feels stretched or unreliable

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

Sticky behaves like a block
Sticky sidebar

Start alignment prevents the sidebar from being stretched in a way that makes sticky behavior harder to reason about.

Error 5

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 like position: stickyed or a class that is not applied.
  • Add a clear offset such as top:0, top:16px, or top: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:start or align-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:fixed for persistent floating UI.
  • Add a background and a useful z-index only after sticky is actually working.
Best first move Add top and test again before changing anything else.
Most common false fix Increasing z-index when the real issue is parent overflow or missing offset.
Most overlooked cause The parent is too short, so sticky has no visible area where it can stay stuck.
Better mindset Sticky is a scroll relationship. Debug the parent chain, not just the element.

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.