# WooCommerce sale badge: change the text, show the percentage off, and fix it on variable products

Source: https://salesdrawer.com/blog/woocommerce-sale-badge-text-percentage-variable-products/ · Published 2026-10-28 · By Sales Drawer team · Category: WooCommerce how-to

> Change the WooCommerce "Sale!" badge text, show the percentage saved, make it work in block themes, and fix the missing badge on variable products.

The default WooCommerce sale badge says "Sale!" and nothing else. You can change the wording with a short filter, show the percentage or amount saved with a slightly longer one, and in block themes you can do most of it from the Site Editor. Variable products need one extra step, because WooCommerce only shows a badge when the parent product counts as on sale. This guide covers all of it, classic and block themes, with copy-and-paste code and a note on when a plugin is the better choice.

## Where the badge comes from

In **classic themes**, WooCommerce prints the badge through a template (`loop/sale-flash.php` and `single-product/sale-flash.php`) and passes the HTML through the `woocommerce_sale_flash` filter. Anything hooked to that filter can change or replace the badge.

In **block themes**, product cards built with the Product Collection or Products blocks use the **On-Sale Badge** block (`woocommerce/product-sale-badge`). Its text and style live in the Site Editor, and WooCommerce's developer docs describe the block and its filter in the [On-Sale Badge block reference](https://developer.woocommerce.com/docs/block-development/extensible-blocks/product-sale-badge-block/).

In both cases the badge appears only when `$product->is_on_sale()` is true, which is why a mis-saved sale price or a stale schedule can make a badge "disappear". If that is your problem, [WooCommerce scheduled sale not working](/blog/woocommerce-scheduled-sale-not-working/) walks through the causes.

## Change the "Sale!" text (classic themes)

Add this to your child theme's `functions.php` or a code-snippets plugin:

```php
add_filter( 'woocommerce_sale_flash', function ( $html, $post, $product ) {
    return '<span class="onsale">' . esc_html__( 'Deal', 'your-textdomain' ) . '</span>';
}, 10, 3 );
```

Keep the `onsale` class so your theme's badge styling still applies.

## Show the percentage off

Shoppers respond to a concrete saving far better than to the word "Sale". For simple products the maths is straightforward:

```php
add_filter( 'woocommerce_sale_flash', function ( $html, $post, $product ) {
    if ( ! $product->is_on_sale() || ! $product->is_type( 'simple' ) ) {
        return $html;
    }
    $regular = (float) $product->get_regular_price();
    $sale    = (float) $product->get_sale_price();
    if ( $regular <= 0 || $sale >= $regular ) {
        return $html;
    }
    $percent = round( ( $regular - $sale ) / $regular * 100 );
    return '<span class="onsale">' . sprintf( esc_html__( 'Save %d%%', 'your-textdomain' ), $percent ) . '</span>';
}, 10, 3 );
```

To show the amount instead ("Save $30"), replace the last line with `wc_price( $regular - $sale )` wrapped in the same `sprintf`.

## Variable products: why the badge is missing, and the "up to" fix

A variable product is on sale when **any** of its variations is on sale, so the badge appears on the parent. Two things go wrong in practice:

1. **The badge doesn't show at all.** The sale price was set on the parent's General tab, which variable products ignore. Set it on each variation (or use the Variations tab's bulk actions). A long-standing [WooCommerce GitHub issue](https://github.com/woocommerce/woocommerce/issues/12933) also describes the related quirk where a variable product shows no crossed-out range when only one variation is discounted.
2. **The percentage snippet above does nothing.** It only handles simple products, because a variable product has many prices. The honest label is the *largest* discount across its variations, as "Up to X% off":

```php
add_filter( 'woocommerce_sale_flash', function ( $html, $post, $product ) {
    if ( ! $product->is_on_sale() || ! $product->is_type( 'variable' ) ) {
        return $html;
    }
    $max = 0;
    foreach ( $product->get_available_variations( 'objects' ) as $variation ) {
        $regular = (float) $variation->get_regular_price();
        $sale    = (float) $variation->get_sale_price();
        if ( $variation->is_on_sale() && $regular > 0 && $sale < $regular ) {
            $max = max( $max, round( ( $regular - $sale ) / $regular * 100 ) );
        }
    }
    if ( ! $max ) {
        return $html;
    }
    return '<span class="onsale">' . sprintf( esc_html__( 'Up to %d%% off', 'your-textdomain' ), $max ) . '</span>';
}, 10, 3 );
```

Keep "up to" in the wording. Stating "30% off" on a product where only one size is 30% off is the kind of claim consumer regulators look at; see [sale pricing rules for WooCommerce stores](/blog/woocommerce-sale-pricing-rules-omnibus-cma-ftc/).

## Block themes: edit the badge in the Site Editor

1. Go to **Appearance → Editor → Templates** and open the template that lists products (for example *Product Catalog*) or the *Single Product* template.
2. Select the **On-Sale Badge** block inside the product card. Change its text, alignment, colours and padding in the block settings.
3. Save. The change applies wherever that template is used.

The block's default text can also be changed in code with the `woocommerce_sale_badge_text` filter described in the developer reference linked above. A percentage badge, as of this writing, still needs a snippet or a plugin; there is an open [request to add "% off" to the core badge](https://github.com/woocommerce/woocommerce/issues/66850), which is a good signal of how often store owners want it.

## Style the badge

Most themes style `.onsale` already. To make it a round, high-contrast badge:

```css
.onsale {
  position: absolute; top: 12px; left: 12px;
  background: #FF632C; color: #fff;
  font-weight: 800; font-size: 13px; line-height: 1;
  padding: 10px 12px; border-radius: 999px;
  min-width: 0; min-height: 0;
}
```

Test on a product page and in the grid, on mobile, because themes position the two differently.

## Do you need a plugin?

A badge plugin earns its place when you want **multiple badges with rules** ("New", "Low stock", "Best seller"), scheduled badge campaigns, image badges, or a settings screen a non-developer can use. For "say the saving instead of Sale!", the snippets above are enough, and they add no weight to your pages.

## The badge solves half the problem

A better badge helps the shopper who is already looking at a sale product. It does nothing for the visitor reading a blog post or arriving on a full-price product page. For that, the sale needs to travel with the shopper: see [how to show sale products on every page](/blog/show-sale-products-on-every-page-woocommerce/).

Sales Drawer takes the same "say the saving" idea site-wide: every product in the drawer shows its regular price, its sale price and the saving, and you can choose the [layout and colours](/docs/layouts-and-appearance/) to match your badge.

## Frequently asked questions

### How do I change the "Sale!" text in WooCommerce?

In classic themes, hook the `woocommerce_sale_flash` filter and return your own `<span class="onsale">` markup. In block themes, edit the On-Sale Badge block in the Site Editor.

### How do I show the percentage off on the sale badge?

Use a `woocommerce_sale_flash` filter that reads the product's regular and sale price, calculates the percentage, and returns it in the badge. For variable products, calculate the largest discount across variations and label it "Up to X% off".

### Why is the sale badge not showing on my variable product?

Usually because the sale price was entered on the parent product instead of on each variation. Set the sale price (and any schedule) on the variations, then check the product again. If only some variations are on sale, the badge still shows, but the crossed-out price range may not.

### Does the sale badge work with block themes?

Yes. Product grids built with the Product Collection or Products blocks use the On-Sale Badge block, which you can edit in the Site Editor. The classic `woocommerce_sale_flash` filter does not affect block-based product cards.

### Do I need a plugin to customise the sale badge?

Not for text or percentage changes; a short snippet handles those. A plugin makes sense when you want several badge types with rules, image badges or a settings screen.

### Why has my sale badge disappeared?

The product is no longer considered on sale: the sale period ended, the sale price was removed or isn't lower than the regular price, or a scheduled sale hasn't switched on yet. Check the product's price fields and WooCommerce → Status → Scheduled Actions.
