WooCommerce: Display % Discount @ Shop Page

Default WooCommerce shows a “Sale” badge if the item is on sale – but what about showing the exact sale percentage instead?

I implemented this for one of my freelance clients so here you go with the easy-peasy solution. Enjoy!

Show discount percentage instead of the default “SALE!” on the WooCommerce product loop

PHP Snippet: Display Discount Percentage @ WooCommerce Shop / Category / Product Archive Pages

/**
 * @snippet       Display Discount Percentage @ Loop Pages - WooCommerce
 * @how-to        Get CustomizeWoo.com FREE
 * @author        Rodolfo Melogli
 * @compatible    WooCommerce 6
 * @donate $9     https://businessbloomer.com/bloomer-armada/
 */ 

add_action( 'woocommerce_before_shop_loop_item_title', 'bbloomer_show_sale_percentage_loop', 25 );

function bbloomer_show_sale_percentage_loop() {
	global $product;
	if ( ! $product->is_on_sale() ) return;
	if ( $product->is_type( 'simple' ) ) {
		$max_percentage = ( ( $product->get_regular_price() - $product->get_sale_price() ) / $product->get_regular_price() ) * 100;
	} elseif ( $product->is_type( 'variable' ) ) {
		$max_percentage = 0;
		foreach ( $product->get_children() as $child_id ) {
			$variation = wc_get_product( $child_id );
			$price = $variation->get_regular_price();
			$sale = $variation->get_sale_price();
			if ( $price != 0 && ! empty( $sale ) ) $percentage = ( $price - $sale ) / $price * 100;
			if ( $percentage > $max_percentage ) {
				$max_percentage = $percentage;
			}
		}
	}
	if ( $max_percentage > 0 ) echo "<div class='sale-perc'>-" . round( $max_percentage ) . "%</div>";
}

[/php]

And a bit of CSS:

.sale-perc {
   background-color: #D9534F;
   display: inline;
   padding: .2em .6em .3em;
   font-size: 75%;
   font-weight: bold;
   color: #fff;
   text-align: center;
   border-radius: .25em;
}

“I don’t code – is there a reliable plugin for that?”

As many readers would love to code but don’t feel 100% confident with it, I decided to look for a reliable plugin that achieves the same (or even better) result.

In this case, I recommend the YITH WooCommerce Badge Management plugin. On top of displaying custom text badges (free version), you can also create CSS, image and advanced badges, assign product badges to specific products and/or categories, pick the badge position and much more.

But in case you wish to code, keep reading ๐Ÿ™‚

Where to add this snippet?

You can place PHP snippets at the bottom of your child theme functions.php file (delete "?>" if you have it there). CSS, on the other hand, goes in your child theme style.css file. Make sure you know what you are doing when editing such files - if you need more guidance, please take a look at my free video tutorial "Where to Place WooCommerce Customization?"

Does this snippet (still) work?

Please let me know in the comments if everything worked as expected. I would be happy to revise the snippet if you report otherwise (please provide screenshots). I have tested this code with Storefront theme, the WooCommerce version listed above and a WordPress-friendly hosting on PHP 7.3.

If you think this code saved you time & money, feel free to join 14,000+ WooCommerce Weekly subscribers for blog post updates or 250+ Business Bloomer supporters for 365 days of WooCommerce benefits. Thank you in advance :)

Need Help with WooCommerce?

Check out these free video tutorials. You can learn how to customize WooCommerce without unnecessary plugins, how to properly configure the WooCommerce plugin settings and even how to master WooCommerce troubleshooting in case of a bug!

Rodolfo Melogli

Business Bloomer Founder

Author, WooCommerce expert and WordCamp speaker, Rodolfo has worked as an independent WooCommerce freelancer since 2011. His goal is to help entrepreneurs and developers overcome their WooCommerce nightmares. Rodolfo loves travelling, chasing tennis & soccer balls and, of course, wood fired oven pizza.

63 thoughts on “WooCommerce: Display % Discount @ Shop Page

  1. Hello, how could I put the total value of the discount instead of the percentage, thanks.

    1. Just change the formula to “regular – sale”

  2. Thank you so much for this. I am using Advanced Dynamic Pricing for WooCommerce to apply discount on multiple products together rather than reducing the price individually. Is it possible to show the badge on the products discounted via Advanced Dynamic Pricing for WooCommerce plugin?

    1. Hi Junaid, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  3. Another Solution from you guys God bless you!

    1. Thanks for the “guys” but it’s only me lol

  4. Sir, Where i have to put this code , ?
    which you have provided code in this post “

    1. Please read the whole post

  5. You are Awesome brother this is help me a lot ..
    thank you for sharing knowledge with us ..
    not this post your all post help me a lot for woocommerce customisation.

    1. Cheers brother

  6. Hi! First of all: I love all your snippets.
    That being said, with the gutenberg blocks we have now the chance to put woocommerce “Featured Product Block”, any chance anyone knows where to hook this action to make it work also on them?

    1. Hi Dan, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  7. Hi,
    Is there any way I can display the discounts percentage off in same line as the price.

    1. Sure, you need CSS for that

  8. I did everything that I could, but I can’t get the results. I am using woo 4.1, where is the problem. Is it in snippet or compatibility with my WooCommerce version of some I don’t know how to copy and past?

    1. Could be anything… what theme are you on? Also, disable all plugins but WooCommerce

  9. If you have a large website with lots of product variants, this code seriously slows down performance of the website and consumes a lot of RAM. This is because the code is loading every child variant into RAM when it only needs access to the sale price and regular price.

    There is another function you can call on the parent product called get_variation_prices which grabs all the prices into an array – far quicker to parse through.

    To get this speed boost, change the foreach loop to this:

          $prices = $product->get_variation_prices( true ); // this is the fastest way to get the prices for all variants - returns multidimensional array from object cache
          foreach($prices['regular_price'] as $pid => $regular_price) {
            if ($regular_price == 0) continue; // if regular price is 0, skip this product
            if ($regular_price == $prices['sale_price'][$pid]) continue; // if sale price = regular price, skip this product
            $percentage = ( $regular_price - $prices['sale_price'][$pid] ) / $regular_price * 100;
            if ( $percentage > $max_percentage ) {
                $max_percentage = $percentage;
            }
          }
    

    I’ve outlined how I figured this out and the improvement here: https://www.wpintense.com/2020/04/29/figuring-out-slow-php-performance-caused-by-loops-using-xdebug/

    I improved speed from 15 seconds page-generation to 1 second. Your mileage will vary depending on how many product variations you have.

    1. Well done!

  10. hello Rodolfo, first of all thank you very much for your all snippets, It is very kind that you are sharing all this useful things! I use divi theme and it works perfect with the shop page.

    In a product when i use one module that shows related products the percentage works also works correct but it is not get centered and it is align on the left and this is kind of ugly…How can i solve this?

    In the shop page works 100% percent correct but in the product page i have this issue.

    Thanks i advance…greetings from Greece!

    1. Hi Stelios! You just need custom CSS

  11. Here is my contribution modifying your code..

    If you want to automatically display a product label on the archive page for products that are marked as “featured”, here is the code.

    add_action( 'woocommerce_before_shop_loop_item_title', 'mostrar_etiqueta_producto_destacado_novedad' );
    	function mostrar_etiqueta_producto_destacado_novedad(){
           global $product;
                  if ( ! $product->is_featured() ) return;
                     echo "<span class='destacado-novedad'>ยกNovedad!</span>"; 
        }

    1) You must replace “woocommerce_before_shop_loop_item_title” with the hook where you want it to be displayed in your case.
    2) You must replace “destacado-novedad” with the css class you want to give the label, and then give it styles with a little css.
    3) You must replace the text “ยกNovedad!” for the text you want displayed on the label.
    4) Give css styles to the span class to make the label look pretty. In my case, they are the following styles:

    span.destacado-novedad {
        color: #fff;
        padding: 1px 10px;
        background-color:#dd6868;
    }
    
  12. How can i show on single product page too?

    1. Mohammad, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  13. Thanks for the code snippet! It works in my shop but the CSS is not working. Plus can you tell me how to show the discount below the price with some text? Just like this https://prnt.sc/r2ehol

    1. Hi Purkait, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

      1. Purkait, To do what you indicate in the photo, you just have to change the hook “woocommerce_before_shop_loop_item_title” by the hook indicated for the position you want, usually it is usually “woocommerce_after_shop_loop_item”.

        As for adding text before the discount percentage, you must replace the line:

        if ( $max_percentage > 0 ) echo "<div class='sale-perc'>-" . round($max_percentage) . "%</div>"; 

        por esta otra:

        if ( $max_percentage > 0 ) echo "<div class='sale-perc'>You save -" . round($max_percentage) . "%</div>"; 
  14. Hello
    If you are using a plugin to make discounts and the snippet doesnt work just replace get_sale_price() with get_price();
    $sale = $variation->get_price();
    get_sale_price() will get you the price from sale price field from each variation (in my case it was empty because i use some discount plugin for that)
    get_price will get you the active price of the product

    1. Nice!

    2. Worked perfectly, thank you so much!

  15. I was looking for this solution to show a percentage discount. They work great. Thanks man!

    1. Great!

  16. Amazing and just what i was looking for, thankyou

    Just one quick question, how do i add the words “off” after the discount amount so it would read 10% off for example

    1. Hi Ashley, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

      1. Hi Ashley,
        As for adding text after the discount percentage, you must replace the line:

        if ( $max_percentage > 0 ) echo "<div class='sale-perc'>-" . round($max_percentage) . "%</div>";

        for this:

        if ( $max_percentage > 0 ) echo "<div class='sale-perc'>You save -" . round($max_percentage) . "%</div>";
        1. I’m sorry.
          The code above is for adding text before the percentage.
          To add text after the percentage, is this code:

          if ( $max_percentage > 0 ) echo "<div class='sale-perc'> -" . round($max_percentage) . "% Off</div>";
          1. Thank you!

  17. https://www.dropbox.com/s/tz5c6bvjtzq3jva/Screenshot%202019-06-07%2011.30.59.png?dl=0

    https://www.dropbox.com/s/m3umnzd2dpiwks5/Screenshot%202019-06-07%2011.31.15.png?dl=0

    Should this work for variations or is our plugin stopping it? Has the correct base percentage but other options only have 10% off.

    1. This only works on the Shop page – does that help?

  18. Rodolfo thanks for the code, I don’t know why but it gave me error Optimum Nutrition my log:

    “PHP Warning: Division by zero in …”

    So I went to my functions.php file and the only code that had a math division on it was this one.

    Do you think I am doing something wrong?

    1. Snippet fixed, let me know ๐Ÿ™‚

  19. It worked on WordPress 5.0.3 / WooCommerce 3.5.4 / Jupiter 6 Theme with a little adjustment. Removed display:inline and changed padding to 10px.

    Would like to use it on Product page in top right corner. Any ideas?

    1. Nice, thanks Ryan! Unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  20. Thank you very much. It works for me on WC 3.3

  21. This worked like a charm. Thanks
    This saved my time

    1. Thank you Faisal! ๐Ÿ™‚

  22. After add this code in my DIVI theme function.php, i can’t upload images from my admin panel and i also can’t see any images in media library

    1. Hey Ankit, thanks for your comment! You should add this to your child theme functions.php

  23. Thank you again for another great snippet!

  24. Thank you! This is very helpful!

  25. Hi Rodolfo,

    For some reason, today I now get the following errors with this code:

    Notice: Undefined variable: highest_percentage

    Notice: WC_Product::get_child is deprecated since version 3.0! Use wc_get_product instead. in /wp-includes/functions.php on line 3831

    Is there a fix for WC 3+?

    1. John, thank you so much! I’ve updated the post with Woo 3.0 compatibility and also removed a couple of bugs ๐Ÿ™‚ Let me know!

  26. Hi Rodolfo,

    Excellent post! Please help me how can i make the percentage on the product of sale price.
    I want this; https://prntscr.com/f3jzzz

    Could you help?

    1. Toggy, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here on the blog. Thanks a lot for your understanding! ~R

  27. Awesome , It works.

    How can I have the same on product detail page ?

    1. Excellent Yash, thanks for your feedback! I suggest to check the single product page hooks (https://businessbloomer.com/woocommerce-visual-hook-guide-single-product-page/), so that you can find the exact position where you can show such discount ๐Ÿ™‚ Let me know!

      1. Thanks I can get on product page.

        I have one more question, how do I show on related products section where upsells are shown , and also show on featured product section in the project?

        Plz guide

      2. Is there any way to show the % discount on Feature product, related products section?

        1. Hello Yash, thanks for your comment! I’m afraid I can’t help this time – this is custom work and unfortunately I can’t provide premium support to free subscribers. Hope this is ok ๐Ÿ™‚

          For your interest, I just launched https://businessbloomer.com/bloomer-armada/, and you might find a suitable plan to cover your requests. Thanks a lot for your understanding!

  28. amazing!
    thank you a lot.
    why can’t i move it with margin inside css?
    it’s stuck in the center no matter what i do.
    could you help?

    1. Awesome Menelaos, thanks for your comment! Just remove the display: inline from the CSS ๐Ÿ™‚

Questions? Feedback? Support? Leave your Comment Now!
_____

If you are writing code, please wrap it between shortcodes: [php]code_here[/php]. Failure to complying with this (as well as going off topic, not writing in English, etc.) will result in comment deletion. You should expect a reply in about 2 weeks - this is a popular blog but I need to get paid work done first. Please consider joining BloomerArmada to get blog comment reply priority, ask me 1-to-1 WooCommerce questions and enjoy many more perks. Thank you :)

Your email address will not be published.