Add Custom CSS to WooCommerce Emails

If you want to add custom CSS styles to your WooCommerce email templates, you can use the woocommerce_email_styles filter.

How to do it

First, you should find out what elements you want to style with CSS. One way to do that is using a plugin like WP Mail Logging which will add a way for you to visually see the emails you’re sending out:

WP Mail Logging – the email log view

If you click on “View” you’ll see the emails:

Example of an Email from WP Mail Logging

Now you can use your developer tools to inspect what you want to change. I want to change the color of the “[Order #526] (October 13,2020)” title.

Inspecting the emails

I can see that this is the H2 tag, so I will target that in the filter. Now let’s write some code! What you want to do is add this code to your theme’s function.php file or to a functionality plugin.

WooCommerce: 4.8.0
Filter: woocommerce_email_styles

<?php  // only include this if it's not already in the file! 

add_filter( 'woocommerce_email_styles', function( $css ) {
    return $css .= '
        h2 { color: blue }

What we’re doing is taking the $css variable, and adding more styles to it with the .= operator. Then, WooCommerce takes these styles and passes them to the Emogrifier which will translate these styles into inline-css to be used in the email. You can read more about the Emogrifier tool and how it works on their repo.

Final result:

<h2 style="font-family: &quot;Helvetica Neue&quot;, Helvetica, Roboto, Arial, sans-serif;font-size: 18px;font-weight: bold;line-height: 130%;margin: 0 0 18px;text-align: left;color: blue">[Order #526] (October 13, 2020)</h2>

One thing I noticed is that the previous h2 style was gone, so I looked it up and I realized that it’s the Emogrifier that’s doing the magic here – it’s only taking the highest priority rules per element and transmogrifying them to inline styles. Also, I need to use the word transmogrifying more. “I’m going to transmogrify this sandwich”.


How to Override WooCommerce Templates – with examples!

Customizing your store experience is one of the main advantages of WooCommerce over competing e-commerce platforms. Need a custom After Order page? No problem! Want to change how your emails look or what they say? Absolutely! You can do it all.

In this post, I want to go over some basics as well as give you an example of how you can customize the WooCommerce “After Order” page template. This is the template that shows up after someone has successfully placed an order on your store. This page is also known as the “Thank You” or “Order Received” page.

Before we do that, let’s dig in to some basics!

What is a template?

A template, in the WordPress world, is a .php file that is used to display a portion of your website. WordPress has a lot of templates, and a single page on your site will load anywhere from 2 to 10+ templates depending on how your theme is built! An example of the theme Twenty Twenty:

Using the Query Monitor plugin, you can inspect what templates are loaded on a given page!

For a homepage displaying latest posts, we can see the Twenty Twenty theme loads the index.php which then calls several different templates in template-parts. The index.php template also calls get_header and get_footer which will get the header.php and footer.php template files from the theme.

WooCommerce-specific templates

Like your theme, WooCommerce also has a set of templates, which you can see inside the woocommerce/templates folder (source). Let’s take a look in that folder:

The WooCommerce template folder as of 4.5.2

Each of those templates is used for a specific part of your store. For example, content-single-product.php will be used to display the content for a single product.

How to override WooCommerce templates

First, you need to make sure WooCommerce knows your theme supports WooCommerce, so that it will look in your theme and use your custom files. You can do that by adding this to your theme’s functions.php file:

function discocowdev_add_woocommerce_support() {
	add_theme_support( 'woocommerce' );
add_action( 'after_setup_theme', 'discocowdev_add_woocommerce_support' );

Next, in order to override a WooCommerce template, you’ll need to copy the template you want to override into a specific folder in your theme. Here’s an example:

If I want to override the template located in:
I would copy that file and place it into:

WooCommerce first looks at your theme for templates, and if it doesn’t find any, it will load the default ones. If it does find a template in your theme, it will load that one instead!

What if my theme is already overriding the template I want to change?

You might have a theme you bought or downloaded that already includes this woocommerce folder. If that’s the case, you have two options:

  1. (Recommended) Make a Child Theme and add your overrides to the child theme.
  2. You can edit the file you want in your theme. However, if you update your theme at any time, your customizations will be gone!

Customizing the Thank You Page

Ok, now that I’ve copied over the thankyou.php file into my theme here: woocommerce/checkout/thankyou.php – I can now make some changes!

Here’s what the default looks like on the Twenty Seventeen theme:

Default Thank You page on the Twenty Seventeen Theme

Let’s break that up into where everything is coming from:

The top part is coming from thankyou.php

The Order Details section is coming from order/order-details.php

The bottom Customer Details section is coming from order/order-details-customer.php

The “Order Received” title of the page is coming from the woocommerce_endpoint_order-received_title hook.

Let’s change the “Thank you. Your order has been received” text. To do that, we’ll edit this line in the template:

Note: You can also edit this through a filter, but for this tutorial we’re just going over how to do it by editing a template 🙂

So this line:

<p class="woocommerce-notice woocommerce-notice--success woocommerce-thankyou-order-received"><?php echo apply_filters( 'woocommerce_thankyou_order_received_text', esc_html__( 'Thank you. Your order has been received.', 'woocommerce' ), $order ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></p>

Will become this:

<p class="woocommerce-notice woocommerce-notice--success woocommerce-thankyou-order-received"><?php echo apply_filters( 'woocommerce_thankyou_order_received_text', esc_html__( 'You\'re the best!', 'woocommerce' ), $order ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></p>

And now we have the new text: “Thank you! You’re the best!” on your Thank You page.

Our newly customized Order Received page!

Hope this was helpful!


How to Hide the Product Price in WooCommerce

I’ve been asked this many times:

How do I remove the price from my shop? I only want it to be seen in the product page

The reasons vary – maybe it’s a site that doesn’t sell but only lists products, maybe it’s an art gallery that doesn’t like to display prices on the main shop page, or maybe it’s a sales tactic.

What do we need to consider?

I think there’s a few things to consider beyond just removing the price. Let’s take a look at a standard Shop page:

Standard WooCommerce Shop page as seen on the Twenty Seventeen theme

Looking at this example, if we removed the price, does it still make sense to have an “Add to Cart” button? Why would you add it to your cart if you didn’t know the price?

Maybe a “Click to view price” link would be better. Or some kind of call to action to view more information.

Here’s our list for what we need to do, it’s pretty short!

  1. Remove the Price for the Shop or any other “Shop loop” pages
  2. Change “Add to Cart” to something else that will send you to the product page itself.

If you’re not sure what “Shop loop” means, the Shop Loop is basically how WooCommerce uses templates to display products in a loop. You can see how the loop works here in the source code.

Please note: The following code should be added to your themes functions.php file OR to a functionality plugin.


Lets remove the price

WooCommerce: 4.5.2

This is pretty straight forward:

<?php  // only include this if it's not already in the file!

remove_action( 'woocommerce_after_shop_loop_item_title', 'woocommerce_template_loop_price', 10 );

What’s happening here?
WooCommerce has a template file called content-product.php (source) that displays each product in the loop. In that template, there are some action hooks, where other things can “hook” into. In this case, the price is being hooked into the woocommerce_after_shop_loop_item_title action. When the template calls that action, it retrieves all things hooked into there, like the price!

What we’re doing is removing the woocommerce_template_loop_price function FROM the woocommerce_after_shop_loop_item_title action hook. When we do that, the price is removed!

Lets see what it looks like now:

WooCommerce Shop page, now without prices!

Let’s change the “Add to Cart” action

WooCommerce: 4.5.2

Very similar to the above change, we first need to remove the “Add to Cart” code that runs on the hook for the shop loop!

<?php  // only include this if it's not already in the file!

remove_action( 'woocommerce_after_shop_loop_item', 'woocommerce_template_loop_add_to_cart', 10 );

Next, we want to now add some text with a link to the product:

<?php  // only include this if it's not already in the file!

add_action( 'woocommerce_after_shop_loop_item', 'discocow_woocommerce_template_loop_custom_link' );
function discocow_woocommerce_template_loop_custom_link() {
	global $product;
	if ( $product ) { 
		$permalink = $product->get_permalink();
		echo '<a href="' . esc_url( $permalink ) . '" class="button addtocartbutton">Learn more</a>';

What does this do? First, we’re hooking into the same action that the Add to Cart was hooked into: woocommerce_after_shop_loop_item. When we hook into that, we need to echo out (which means we’re spitting out into the browser) our new link. To do that, we need to get the global $product that’s currently being called, grab it’s link, and then safely wrap it around an anchor tag.

Here’s the final result:

Final result of our customization!

Final thoughts

WooCommerce has some pretty powerful template hooks. If you want to explore how you could customize your site, here are some good resources:

The current WooCommerce Template files source

The official documentation on how to override templates (that I helped write)

The WooCommerce Wiki on Template Guidelines for Developers and Theme Authors

If you have questions on this topic or want me to cover anything else, please let me know in the comments!


Increase the default number limit of variations for a product in WooCommerce

In WooCommerce, you can add as many variations as you want! However, if you have over 30 of them, WooCommerce will make an AJAX call (an extra request to the backend) to fetch the details for each variation.

Why is the limit even there?

While it can be annoying, I think it’s a good idea to have a general limit. The general idea is that if you have too many variations, it’s going to make it really difficult for a customer to choose what to buy. If you have over 30 variations, you might want to think of splitting it up into more products so it’s not so confusing for people.

However, in some use cases, 30 variations is not a lot! For example, shoes and clothing items can easily have more than 30 variations and still be totally acceptable. That’s because it’s less of a choice for the customer and more of figuring out what size they need.

Lets change the default limit

To change the limit, we’re going to use a filter!

WooCommerce: 4.5.2
Filter: woocommerce_ajax_variation_threshold

The code:

<?php // leave this line out unless you're starting a new PHP file

add_filter( 'woocommerce_ajax_variation_threshold', function( $quantity, $product ) {
	return 50;
 }, 10, 2 );

In that code, we’re just returning a new quantity of 50 for the maximum value allowed for variations.

If you wanted to do this for just a single product, here’s what that would look like:

<?php // leave this line out unless you're starting a new PHP file

add_filter( 'woocommerce_ajax_variation_threshold', function( $quantity, $product ) {
	// If you want change the default limit for just one product, set the ID here:
	if ( 401 === $product->get_id() ) {
		return 100;
 }, 10, 2 );

If you have any questions or suggestions for improvements, let me know!


Add a Custom Placeholder Image for WooCommerce Products

WooCommerce has a default placeholder for all products that don’t have images set. Here’s what it currently is set to:

Default WooCommerce Placeholder Image

I actually like it quite a lot, but it might not always be the most useful thing to show for your specific store.

If you want to change the default placeholder for your WooCommerce store, it’s not too complicated. Let’s walk through what has to happen:

The Source

I like to cover what code we’re working with first. If you do this regularly you’ll eventually be able to do this on your own 🙂

WooCommerce: 4.5.2
Filter: woocommerce_placeholder_img_src

Working through the logic

The function, wc_placeholder_img is where the placeholder images are loaded from, so looking at that function, we can see that it checks for:

  • The woocommerce_placeholder_image option being set to a specific image ID
  • If that option is not set, it calls the wc_placeholder_img_src
  • wc_placeholder_img_src function then calls the filter, woocommerce_placeholder_img_src, that lets us change the image source for the placeholder!

Our code should then:

  • Set the ID for the image we want to be the new placeholder
  • Make sure we set the option to the right image if we need to
  • Return the right image source only if the option is not set

Here’s the code:

<?php // only add this first line if your file already does not have it

 * Change the default placeholder image
add_filter('woocommerce_placeholder_img_src', 'custom_woocommerce_placeholder_img_src', 10);

function custom_woocommerce_placeholder_img_src( $src ) {
	// Set your new Placeholder Image ID here
	$new_image_id = 400;

	// Update the woocommerce_placeholder_image option
	$current_placeholder_id = get_option( 'woocommerce_placeholder_image' );
	if ( $current_placeholder_id !== 0 || $current_placeholder_id !== $new_image_id ) {
		update_option( 'woocommerce_placeholder_image', $new_image_id );
	} else {
		// Set the Image SRC
		$src = wp_get_attachment_url( $new_image_id );

	return $src;

Make sure you upload the Image and grab the ID for that image for that code to work. If you’re not what the Image ID is, go to the Media Library and click on the image. Then, in the address bar you should see the ID like this:

The ID is 400 for this image

Tested on

WooCommerce 4.5.2 ✔
Storefront 2.7.0 ✔

That’s it! If you have any questions or suggestions for how to improve this, please leave a comment.