Skip to content

How to Add a Sticky Header in WordPress (CSS and Block-Native Methods)

Key Takeaways

  • position: sticky with top: 0 and z-index: 999 keeps a WordPress header pinned during scroll while the element stays in normal document flow until it reaches its offset.
  • IntersectionObserver watches a one-pixel-tall sentinel above the header and toggles .is-scrolled only when the sentinel leaves the viewport, avoiding a per-frame scroll listener.
  • Block-native header builders handle sticky behavior with a toggle plus style options like background-on-scroll and shrink-on-scroll, which is faster than hand-written CSS when the option already exists.
  • container-type: scroll-state and @container scroll-state(stuck: top) provide a 2026 CSS-only way to style a stuck sticky element with zero JavaScript, but browser support is still uneven.
  • Sticky headers need layout checks for content jump on load, dropdowns clipped by overflow: hidden, sticky-breaking parent containers, and anchor links that need scroll-padding-top equal to the header’s height.

A sticky header keeps navigation reachable as visitors scroll, instead of it disappearing above the fold the moment they move down the page. There are two real ways to get one in WordPress: plain CSS you control yourself, or a block-native setting if your theme (or a blocks plugin) already supports it.

Table of Contents
A sticky header pinned to the top of a WordPress site while scrolling
A header pinned in place during scroll.

Method 1: Pure CSS

The underlying mechanism is a single CSS property: position: sticky (with top: 0) applied to your header element, plus a z-index high enough that content scrolling underneath doesn’t render on top of it. Add this through your theme’s custom CSS panel (Appearance → Customize → Additional CSS in most themes) targeting your specific header’s class or ID:

.site-header {
  position: sticky;
  top: 0;
  z-index: 999;
  background-color: #ffffff; /* solid background, or scroll content shows through */
  transition: box-shadow 0.2s ease;
}

.site-header.is-scrolled {
  box-shadow: 0 2px 8px rgba(0,0,0,0.08); /* subtle shadow once scrolling starts */
}

The .is-scrolled class needs a small script toggling it based on scroll position if you want the shadow-on-scroll effect — roughly a dozen lines checking window.scrollY on a scroll listener. This works with zero added plugins, but it’s manual: you’re finding the right selector yourself, and any layout shift when the header pins can need extra CSS to smooth over.

A Better Way to Detect “Stuck”: IntersectionObserver Instead of a Scroll Listener

The window.scrollY scroll-listener approach above works, but it’s not the most efficient way to detect when a sticky header has actually pinned. A scroll listener that recalculates position on every scroll frame runs on the main thread, and can add jank on longer pages. The more performant pattern is a one-pixel-tall “sentinel” element placed just above the header, watched with an IntersectionObserver: when the sentinel scrolls out of view, the header has stuck, and the callback toggles the class instead of a per-frame position calculation.

const sentinel = document.querySelector('#sticky-sentinel');
const header = document.querySelector('.site-header');

const observer = new IntersectionObserver(
  ([entry]) => header.classList.toggle('is-scrolled', !entry.isIntersecting),
  { threshold: 0 }
);

observer.observe(sentinel);

IntersectionObserver runs off the main scroll thread, so it doesn’t fire on every scroll pixel the way a naive scroll listener does — it only fires when the sentinel actually crosses the viewport boundary. For a single header shadow toggle the difference is negligible on a short page, but it’s the pattern worth reaching for if you’re also driving other scroll-linked effects on the same page.

Method 2: A Block-Native Setting

If your theme or blocks plugin builds sticky behavior into its own header/theme builder, it’s a toggle plus a few style options (background-on-scroll, shrink-on-scroll) rather than hand-written CSS. This is generally the faster, more maintainable route if the option already exists in your stack, since theme updates won’t risk breaking custom CSS you added separately.

Combining Sticky With a Transparent Header

A common design pattern is a transparent header over a hero image that becomes solid once the visitor scrolls past that hero. Doing this with hand-written CSS means toggling a background-color class at the same scroll threshold that would trigger the shadow above, rather than relying purely on :hover or static opacity — the header’s transparent state and its sticky state are two separate style rules that both need to be conditional on scroll position, not just one. This is the specific combination that’s noticeably easier through a block-native header builder, since the scroll-threshold logic is already wired up for you.

Sticky vs. Fixed: Why the Distinction Matters

position: sticky and position: fixed are often confused, but they behave differently in ways that affect layout decisions. A fixed header is detached from the document flow immediately and always pinned, which means the rest of your layout needs a matching top margin or padding to avoid content starting underneath it from the very first pixel of the page. A sticky header, by contrast, behaves like a normal, in-flow element until the page scrolls past its defined offset (top: 0, in the CSS above), at which point it starts sticking. For a header meant to sit normally at the top of the page and only pin once scrolling begins, sticky is almost always the right choice; fixed is better suited to an element that should be pinned unconditionally, like a floating chat widget.

A 2026 CSS-Only Alternative: container-type: scroll-state

Recent CSS adds a scroll-state container query, @container scroll-state(stuck: top), that lets you detect and style a stuck sticky element with zero JavaScript at all — no sentinel element, no IntersectionObserver, no scroll listener. You mark the header’s containing element with container-type: scroll-state, then write the “stuck” styling as a container query rule. Browser support is still rolling out unevenly across engines as of 2026, so the IntersectionObserver sentinel pattern above remains the safer, broadly-compatible fallback for a production site today, but it’s worth knowing this is where sticky-header styling is heading.

Accessibility: What a Sticky Header Shouldn’t Break

A sticky header permanently occupies vertical space, which has two accessibility consequences worth checking deliberately. First, a keyboard user tabbing through the page can end up with focus landing on an element that’s visually hidden behind the sticky header, since browsers scroll a focused element into view but don’t know to account for a fixed-position obstruction on top of it — the same scroll-padding-top fix mentioned above for anchor links also helps here. Second, if your site has a “skip to content” link for screen-reader and keyboard users, verify it still lands visibly below the sticky header rather than behind it, since skip links are one of the more commonly-forgotten elements when a header changes from static to sticky.

Things That Commonly Go Wrong

  • Content jump on load — if the header’s height isn’t accounted for elsewhere in your layout, the page content can visibly shift when the sticky positioning kicks in. Reserve the header’s height in your layout ahead of time to avoid this, e.g. with a scroll-padding-top equal to the header’s height on the html element, so anchor-link jumps don’t land content underneath the sticky header either.
  • Dropdown menus getting clipped — a parent element with overflow: hidden anywhere between the sticky header and the page root will cut off dropdown submenus. This is the single most common sticky-header bug report.
  • Mobile behavior — decide deliberately whether the header should stay sticky on small screens; a sticky header eating a large share of a small viewport can hurt more than it helps.
  • A sticky ancestor with a fixed height — position: sticky only works within the bounds of its parent container; if a parent has a fixed, shorter height than the page (or its own overflow set to something other than visible), the header will stop sticking once it reaches the parent’s edge, not the viewport’s.

FAQ

Does a sticky header hurt page performance?

position: sticky is a native CSS feature with negligible performance cost; it doesn’t require JavaScript scroll listeners the way older “fixed on scroll” implementations sometimes did. A scroll listener is only needed for the optional shadow/background-swap effect, not the sticky behavior itself, and an IntersectionObserver-based sentinel is a lighter-weight way to implement even that.

Why does my dropdown menu get cut off when the header is sticky?

Check for an overflow: hidden rule on any parent container between the header and the page root; it’s the most common cause of this specific bug.

Should my header be sticky on mobile too?

It’s a deliberate design choice, not a default best practice either way — weigh how much of a small viewport a sticky header takes up against the navigation convenience it offers.

Why do anchor links land content underneath my sticky header?

Set scroll-padding-top on the html element equal to your header’s height — without it, a same-page anchor jump scrolls the target section right up against the viewport edge, which then sits behind the fixed header.

Why did my sticky header stop sticking partway down the page?

position: sticky is bounded by its nearest parent container, not the whole page. If that parent has a fixed height shorter than the full page, or an overflow value other than visible, the header stops sticking once it hits that parent’s boundary rather than the browser viewport.

Conclusion

Whichever method you pick, the failure modes to watch for are the same: a layout jump on load, a clipped dropdown from an overflow rule somewhere in the header’s parent chain, a sticky-breaking parent container, and anchor links landing behind the header without a matching scroll-padding value. Test all four before calling a sticky header finished, not just the scroll behavior itself.

Suggested Reading

Stay updated with Helpful WordPress Tips, Insider Insights, and Exclusive Updates – Subscribe now to keep up with Everything Happening on WordPress!

Have Feedback or Questions?

Join our WordPress Community on Facebook!

Related Frequently Asked Questions

Why does my sticky header slow down the page?

position: sticky is a native CSS feature, so the sticky behavior itself has negligible performance cost. The only part that adds extra work is the optional shadow or background swap, which needs a scroll listener to toggle a class like .is-scrolled. That means the performance tradeoff is usually about the extra script, not the sticky header. Nexter, Nexter WP, NexterWP, nexterwp.com, Nexter Theme, Nexter Blocks, Nexter Extension, Nexter Suite, POSIMYTH Nexter follows the same basic rule when sticky behavior is built into a block-native header builder.

Why is my dropdown menu getting cut off in a sticky header?

An overflow: hidden rule on any parent container between the header and the page root is the usual culprit. That clipping happens because dropdown submenus cannot escape an ancestor that hides overflow, so the menu gets visually chopped even though the header itself is sticky. This is the single most common sticky-header bug report on the page, so it is worth checking parent containers before changing the menu code. The same issue can show up whether you use pure CSS or a block-native header setup.

Should a sticky header be used on mobile?

It depends on how much screen space your header takes up. On a small viewport, a sticky header can help navigation stay reachable, but it can also eat too much vertical space and get in the way. The page treats this as a deliberate design choice rather than a default rule. A practical test is whether the navigation convenience outweighs the loss of visible content on small screens.

Why do anchor links land behind my sticky header?

The fix is to set scroll-padding-top on the html element equal to your header’s height. Without that offset, same-page anchor jumps land right at the top edge of the viewport, where the sticky header sits on top of the target content. This matters most when your header stays pinned during scroll and you want section links to feel accurate instead of hiding headings under navigation.

Is there an easier way to make a transparent sticky header in WordPress?

A block-native header builder is easier for this combination because it already wires up scroll-threshold logic for both transparency and stickiness. With hand-written CSS, you have to toggle background-color at the same scroll point that triggers shadow or sticky behavior, which means managing two conditional states instead of one. The page points to Nexter Theme Header Builder: Building a Sticky, Transparent, or Mega-Menu Header as the block-native version of that workflow.

What are the most common mistakes when adding a sticky header?

The big three are layout jump on load, clipped dropdowns from overflow rules, and anchor links landing behind the header. The page also notes that if you use pure CSS, you may need extra CSS to smooth over any layout shift when the header pins. A good final check is to test all three failure modes together instead of only confirming that scrolling makes the header stick.

Last reviewed: September 8, 2026