Skip to content
ThemesIonic — home
WooCommerce

Product Badges in WooCommerce

WooCommerce already outputs a sale badge and gives you a filter to change it. Most badge plugins are a settings screen over the same two template hooks you could use directly.

4 min read intermediate

WooCommerce ships one badge and one filter, and the filter is more capable than most people realise. The sale flash is printed on the catalogue and on the product page, its markup passes through a filter before output, and custom badges attach to the same template hooks the thumbnail uses. Before buying a badge plugin, be clear that what you are buying is a settings screen, some preset conditions and a design picker — not a capability that WooCommerce lacks.

What core does already

On archive listings, WooCommerce prints the sale badge inside the loop item, before the title and alongside the thumbnail. On the product page it prints it before the image gallery. Both output a small element with the class onsale, and both pass that markup through the woocommerce_sale_flash filter, which receives the markup, the post and the product object. Returning different markup from that filter changes every sale badge on the site in one place.

Whether a product counts as on sale is decided by WooCommerce, not by you: a sale price must be set, be lower than the regular price, and fall within its scheduled dates if any are set. For a variable product, the badge appears when any variation qualifies. That is worth knowing before you promise a customer that the badge means the price they see is discounted — if you are still structuring product variations, the badge behaviour follows from how you set prices at variation level.

Adding your own badges

Custom badges are conditional markup printed into the loop item. The hooks are the ones the thumbnail already uses: the badge belongs alongside the image so it can be positioned over it. The pattern is always the same three parts:

  • A condition read from the product object — publish date for "new", stock quantity for "low stock", a taxonomy term or custom field for anything editorial.
  • A small piece of markup with a class you control, printed on the appropriate hook.
  • CSS that positions it absolutely inside the image wrapper, which must itself be positioned. The badge also needs to sit above the product link's own stacking context, or it will be visible but unclickable and, worse, will swallow clicks meant for the card.

That last point is where hand-built badges usually go wrong. The rest is a few lines of custom CSS and a small snippet.

The badges people actually want

Badge Data it needs Difficulty
Sale Native None
Percentage off Regular and sale price Easy for simple products
Percentage off, variable product Prices across all variations Genuinely awkward
New Publish date and a threshold Easy
Low stock Stock quantity, stock management enabled Easy, but caching-sensitive
Out of stock Stock status Easy
Best seller Accumulated sales count Easy to read, expensive to sort by
Editorial, such as "staff pick" A taxonomy or custom field Easy, and the most useful

The variable product row is the one that catches people out. There is no single discount for a product whose variations are discounted differently, so you have to decide what the badge means — the largest discount available, the smallest, or nothing at all — and say so in the label. "Up to 40% off" is honest; a bare "40% off" on a product where one variation is discounted is not.

Editorial badges are underrated. A taxonomy a shop manager can tick does more for merchandising than any automatic condition, and it needs no plugin.

Where badges break

Caching. Badges derived from stock or sales are computed when the page is generated and then frozen into whatever cache holds it. "Only 2 left" is embarrassing when it is three days old. Either keep stock-derived badges off cached pages or accept the staleness deliberately, and understand which pages your cache is holding.

Duplication. Themes frequently print their own sale badge as well as, or instead of, the core one. Adding a plugin then gives you two. If a badge appears twice, look for a theme template override before assuming a bug.

Layout. Badges sit over images, so they inherit every image problem. A badge positioned for a square thumbnail lands in the wrong place on an uncropped one, which is one more reason to settle your product image sizes before designing the card. How the badge relates to the rest of the card is part of the same decision as the product card design.

Truthfulness. Consumer pricing rules in many markets require a reference price to be a price genuinely charged recently. Badges that manufacture a discount by inflating the regular price are a legal exposure, not a merchandising tactic.

What to install, if anything

If you want the sale badge to say something else, that is a filter. If you want two or three conditional labels, that is a snippet and some CSS. Buy a plugin when the store's merchandising team needs to create and schedule badges themselves without a developer, because that workflow — rules, scheduling, a visual editor — is the real product. Everything below it, core already does.

Frequently asked

The badge markup passes through the `woocommerce_sale_flash` filter before it is printed, so a short filter returns whatever markup you want in its place. Overriding the sale flash template files achieves the same thing if you already maintain template overrides.
WooCommerce shows it only when a product is genuinely on sale, meaning a sale price is set and lower than the regular price, and any scheduled dates are current. For variable products it appears when at least one variation is on sale, which is why a badge can appear on a product whose displayed price has not changed.
Because the archive page is being served from a cache created when the stock level was different. Anything computed per request and printed into cached HTML will go stale, so stock-sensitive badges need to be excluded from full-page caching or loaded separately.

Related guides