Adding a WordPress Notification Bar
A notification bar is the least intrusive way to announce something sitewide. The decisions that matter are placement, whether it pushes content down, how dismissal is remembered and how it behaves under page caching.
Some links on this page are affiliate links: if you buy through one, we may earn a commission at no extra cost to you. It does not change what we recommend; see the editorial policy.
A notification bar is one line of text across the top or bottom of the page — the hard part is adding it without pushing the page around. Get placement, layout shift and dismissal right and it is the least intrusive way to announce a sale, a shipping deadline or a policy change. Get them wrong and it costs you a Core Web Vitals metric on every page.
What a bar is for
Good uses are short, sitewide and time-bound:
- Shipping cut-offs and holiday closures.
- A sale or free-shipping threshold, sometimes with a countdown timer to a real end date.
- Maintenance windows and service notices.
- A link to a new product, article or event.
A bar is not a substitute for a signup form or a cookie notice. If it needs more than one sentence and one link, it belongs on a page. If it collects consent, it belongs in a cookie consent tool.
Placement and behaviour
| Choice | Advantage | Cost |
|---|---|---|
| Top, static | Seen first, scrolls away | Pushes the header down |
| Top, sticky | Always visible | Stacks with a sticky header, eats screen on mobile |
| Bottom, sticky | Does not touch the header | Collides with cookie banners and chat widgets |
| Bottom, static | Invisible to most visitors | Rarely worth it |
Top and static is the safe default. Sticky bars on mobile take a fixed slice of a small screen for the whole visit; if you make the bar sticky, make it dismissible.
Avoid layout shift
A bar that appears after the page renders and pushes the content down is a textbook cumulative layout shift. Two ways round it:
- Render it in the HTML from the start. If the bar is in the server output, the browser lays it out before first paint and nothing moves.
- Overlay it. A
position: fixedbar sits on top of the page and shifts nothing, at the cost of covering part of it.
What causes the shift is the common plugin pattern: an empty page loads, then JavaScript decides to inject the bar. If you use a plugin, check whether it outputs the markup server-side or injects it later, and measure before and after.
The no-plugin version
Block theme
Open Appearance → Editor → Patterns, then Header under Template parts, insert a Group block above the existing header contents, give it a background colour and a paragraph with your message, and save. It renders on every template that uses the header. To remove it, delete the group. Template parts are explained in WordPress template parts.
Classic theme
Themes that call wp_body_open() let you print markup right after the opening <body> tag. Add this to a child theme's functions.php or a small custom plugin:
<?php
add_action( 'wp_body_open', function () {
echo '<div class="site-notice" role="region" aria-label="' . esc_attr__( 'Announcement' ) . '">'
. esc_html( 'Orders placed after 18 December arrive after the holidays.' ) . ' '
. '<a href="' . esc_url( home_url( '/shipping/' ) ) . '">Shipping details</a>'
. '</div>';
} );
.site-notice {
min-height: 2.5rem;
padding: 0.5rem 1rem;
background: #1d2327;
color: #fff;
text-align: center;
font-size: 0.9375rem;
}
.site-notice a { color: #fff; text-decoration: underline; }
Because the bar is in the HTML, it causes no shift. The trade-off is that changing the message means editing code, and there is no dismissal. The functions.php guide covers where to put the snippet safely.
Dismissal that sticks
A close button must remember its choice, or the bar returns on every page. Store the dismissal in local storage or a cookie, keyed to the message so a new announcement shows again:
- Hide the bar with a class on
<html>set by a tiny inline script in the head, before first paint. Hiding it later produces a flash and a shift. - Use a real
<button>with an accessible name such as "Dismiss announcement". - Reset dismissal when the message text changes, not on a fixed timer.
Caching and dynamic messages
Page caching serves the same HTML to everyone. That is fine for a sitewide message and wrong for anything personal — "Free shipping: you are £12 away" cannot be baked into a cached page. Personal or cart-aware messages need to be filled in by JavaScript after load, which brings back the layout-shift question: reserve the bar's height in CSS so the text arriving later moves nothing.
Scheduled messages have a similar trap. A bar set to disappear at midnight stays until the cached page expires. Purge the cache when the message changes — clearing the WordPress cache covers each layer.
When a plugin is worth it
Reach for a tool when you need scheduling, page-level targeting, different messages for new and returning visitors, or testing which wording converts. Hosted lead-capture services such as OptinMonster include a floating bar campaign type with those rules on their paid tiers, at the cost of a subscription and a third-party script on every page. A dedicated bar plugin is lighter if all you need is a dismissible message — see which banner plugin you need. Either way, weigh the script against the popup plugin criteria: does it load conditionally, does it output markup server-side, does it respect the cache.
Accessibility
- Contrast of at least 4.5:1 between text and background, including the link.
- Do not use
role="alert"for a static announcement; screen readers would interrupt the visitor on every page load. - Keep the bar short — one link — so it costs keyboard users one extra Tab at most; in most classic themes
wp_body_openoutput lands before the skip link, so check the order with Tab. - Do not animate it in on a loop.
Common mistakes
- Injecting the bar with JavaScript after render, then wondering why CLS went up.
- A sticky top bar plus a sticky header, leaving a third of a phone screen for content. If the header is sticky, offset it by the bar height — see Elementor sticky header for the same issue.
- Bottom bar on top of the cookie banner, so neither close button can be tapped.
- Dismissal that is never read, so the bar reappears on every page.
- Forgotten bars announcing a sale that ended last month.
Verify
- Load a page with throttling in DevTools and confirm no layout shift is attributed to the bar.
- Dismiss it, navigate, and confirm it stays hidden without flashing.
- Check it on a narrow phone alongside the cookie notice and any chat widget.
- Tab through the page and confirm the skip link and dismiss button are reachable.
Frequently asked
- No. A static announcement bar is a group block in the header template part of a block theme, or a few lines of PHP on the wp_body_open hook in a classic theme. A plugin earns its place when you need scheduling, targeting, dismissal memory or A/B testing.
- Because it is inserted after the page has rendered and pushes everything below it down, which counts as cumulative layout shift. Render it in the HTML from the start, reserve its height, or overlay it on top of the page instead of inserting it into the flow.
- Usually the dismissal is stored in a cookie or local storage that the bar's script never reads, or the page cache serves HTML that already contains the bar and the script runs too late to hide it. Hide it with CSS before first paint based on the stored value.