In this post I’m going to show you how to customize Woocommerce shop and category page. Those hacks are really useful for every Woocommerce shop and they are really easy to use.
In order to use all those snippets shown in this post you need add them inside your child theme’s functions.php file or better yet, use Code Snippets plugin. This way you’re not going to lose these modifications in case you’re switching your theme.
WpCodeBox is my favorite code snippets manager for WordPress. This is a premium plugin and if you’re interested, then grab WPCodeBox with a nice 20% discount here (SAVE 20% Coupon WPSH20).
Also, take a look at the video tutorial here below and then it will probably be a bit easier to understand what is what.
Video: How to customize Woocommerce shop and category page?
How to Add conditional Woocommerce product category page messages
With the help of this code snippet here you can add a message to all Woocommerce product category pages.
add_action( 'woocommerce_before_main_content', 'shop_message', 20 );
function shop_message() {
echo '<p class="woocommerce-message">Free shipping for all orders
// Custom message on Woocommerce shop page and all category pages
add_action( 'woocommerce_before_main_content', 'shop_message', 20 );
function shop_message() {
echo '<p class="woocommerce-message">Free shipping for all orders</p>'; // Change your message here
}
How to add message to specific Woocommerce category page?
If you need to add a message to the scpecific Woocommerce product category page (Storage catogory, for example) then use this code instead.
add_action( 'woocommerce_before_main_content', 'category_message', 20 );
function category_message() {
if ( is_product_category( 'storage' ) ) { // Change your vategory slug here
echo '<p class="woocommerce-message">Estimated shipping time: 2-3 weeks
// Message for specific category
add_action( 'woocommerce_before_main_content', 'category_message', 20 );
function category_message() {
if ( is_product_category( 'storage' ) ) { // Change your vategory slug here
echo '<p class="woocommerce-message">Estimated shipping time: 2-3 weeks</p>'; // Change your message here
}
}
How to add one message to specific Woocommerce category page and another message to all other category pages?
Now lets add a one message to the Sotrage category page and another message to all other categories.
add_action( 'woocommerce_before_shop_loop', 'conditional_message', 1 );
function conditional_message() {
if ( is_product_category( 'storage' ) ) { // Change your vategory slug here
echo '<p class="woocommerce-message">Estimated delivery time: 2-3 weeks</p>';// Change your Storage category message here
} else {
echo '<p class="woocommerce-message">Estimated delivery time: 1 week
// One message for specific category (storage) and another for all other categories
add_action( 'woocommerce_before_shop_loop', 'conditional_message', 1 );
function conditional_message() {
if ( is_product_category( 'storage' ) ) { // Change your vategory slug here
echo '<p class="woocommerce-message">Estimated delivery time: 2-3 weeks</p>';// Change your Storage category message here
} else {
echo '<p class="woocommerce-message">Estimated delivery time: 1 week</p>'; // Change your shop-wide message here
}
}
How to add a Woocommerce product category dropdown and attributes filter above the product loop?
Step 1: Create a widget area
With the help of this code snippet here below we create a widget area and add it above the Woocommerce product loop.
add_action( 'widgets_init', 'product_filter_widget_area' );
function product_filter_widget_area() {
register_sidebar( array(
'id' => 'product_filter_sort',
'name' => __( 'Product filter widgets', 'themename' ),
'description' => __( 'Here goes your filter items', 'themename' ),
'before_widget' => '<div class="product-category-dropdown">',
'after_widget' => '</div>',
'before_title' => '<h3 class="widgettitle">',
'after_title' => '
// Create a new widget area called Product Category filter
add_action( 'widgets_init', 'product_filter_widget_area' );
function product_filter_widget_area() {
register_sidebar( array(
'id' => 'product_filter_sort',
'name' => __( 'Product filter widgets', 'themename' ),
'description' => __( 'Here goes your filter items', 'themename' ),
'before_widget' => '<div class="product-category-dropdown">',
'after_widget' => '</div>',
'before_title' => '<h3 class="widgettitle">',
'after_title' => '</h3>'
) );
}
// Add this widget area on top of the category and shop pages
add_action( 'woocommerce_before_shop_loop','add_widget_area' ); // Hook it after headline and before loop
function add_widget_area() {
if ( is_active_sidebar( 'product_filter_sort' ) ) {
dynamic_sidebar( 'product_filter_sort' );
}
}
Step 2: Add Woocommerce product category and filter by attributes widgets
Now go to Appearance >> Widgets and add your Woocommerce Product Categories filter in “Product filter widgets” area. Set it to show as dropdown.
Next add your Filter by attributes widgets and set them to show as dropdown
Step 3: Add custom CSS
Go to Appearace >> Customize >> Additional CSS and this piece of CSS.
@media only screen and (min-width: 668px) {
.product-category-dropdown {
margin: 0px 10px 20px 0px;
display: inline-block;
width: 32.5%;
}
}
@media only screen and (max-width: 667px) {
.product-category-dropdown {
margin-bottom: 10px;
}
}
The end result should look like this.
How to remove Woocommerce Uncategorized product category from shop and category pages?
This code here below hides Uncategorized category from shop page
// Hide Uncategorized product category from shop page
add_filter( 'woocommerce_product_subcategories_args', 'hide_uncategorized_cat_from_shop_page' );
function hide_uncategorized_cat_from_shop_page( $args ) {
$args['exclude'] = get_option( 'default_product_cat' );
return $args;
}
How to remove Woocommerce Uncategorized product category from Product category widget?
This code here below hides Uncategorized category from the sidebar widget
// Hide Uncategorized product category from widget
add_filter( 'woocommerce_product_categories_widget_args', 'hide_uncategorized_cat_from_widget' );
function hide_uncategorized_cat_from_widget( $args ) {
$args['exclude'] = get_option( 'default_product_cat' );
return $args;
}
How to remove category product count in product archives?
As you probably have seen the category loop has a product count shown next to the category name. This code will hide Woocommerce product count in product archives.
add_filter( 'woocommerce_subcategory_count_html', '__return_false' );
How to show the Discount Percentage on the Woocommerce Sale Badge?
By default Woocommerce shows a badge with the “Sale” text. If you would kike to show a discount percentage instead, then use this code here below.
function sale_badge_percentage() {
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 "<span class='onsale'>-" . round($max_percentage) . "%
add_action( 'woocommerce_sale_flash', 'sale_badge_percentage', 25 );
function sale_badge_percentage() {
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 "<span class='onsale'>-" . round($max_percentage) . "%</span>"; // If you would like to show -40% off then add text after % sign
}
How to Display “NEW” Badge for Woocommerce Recent Products that are less than 30 days old?
Step 1: Add the code snippet here below
add_action( 'woocommerce_before_shop_loop_item_title', 'new_badge', 3 );
function new_badge() {
global $product;
$newness_days = 30; // Number of days the badge is shown
$created = strtotime( $product->get_date_created() );
if ( ( time() – ( 60 * 60 * 24 * $newness_days ) ) < $created ) {
echo '<span class="new-badge onsale">' . esc_html__( 'NEW', 'woocommerce' ) . '
// New badge for recent products
add_action( 'woocommerce_before_shop_loop_item_title', 'new_badge', 3 );
function new_badge() {
global $product;
$newness_days = 30; // Number of days the badge is shown
$created = strtotime( $product->get_date_created() );
if ( ( time() - ( 60 * 60 * 24 * $newness_days ) ) < $created ) {
echo '<span class="new-badge onsale">' . esc_html__( 'NEW', 'woocommerce' ) . '</span>';
}
}
Step 2: Customize your badge
Pay attention thought that the CSS shown here below may need tweaking and it depend on your theme. So, tweak it accordingly.
// “NEW” Badge for Woocommerce Recent Products that are less than 30 days old
.woocommerce ul.products li.product .new-badge.onsale {
background: #ffcc00;
top: 50px;
z-index: 10;
left: 0px;
color: #000;
font-weight: 700;
text-transform: uppercase;
font-size: 0.9em;
border-radius: 0px;
min-width: 60px;
padding-left: 19px !important;
}
span.new-badge.onsale:after {
border: 5px solid #ffcc00;
border-color: transparent transparent #ffcc00 #ffcc00;
border-width: 9px 6px;
position: absolute;
right: -10px;
bottom: 0;
content: '';
}
span.new-badge.onsale:before {
border: 5px solid #ffcc00;
border-color: #ffcc00 transparent transparent #ffcc00;
border-width: 9px 6px;
position: absolute;
right: -10px;
top: 0;
content: '';
}
If you would like to show it as a vertical ribbon then add this piece of CSS.
span.new-badge.onsale {
min-width: 60px;
-moz-transform: rotate(90deg);
-webkit-transform: rotate(90deg);
-o-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
}
How to display Woocommerce product category name above the product title?
This solution contains two parts.
add_action( 'woocommerce_shop_loop_item_title', 'show_category_on_loop', 1 );
function show_category_on_loop() {
global $product;
$terms = get_the_terms( $product->get_id(), 'product_cat' );
if( $terms ) {
$names = array();
foreach ( $terms as $term ) {
$names[] = $term->name;
}
print '<span class="category-text"> '.join( ', ', $names ).'
// Display Woocommerce product category name above the product title on product category page
add_action( 'woocommerce_shop_loop_item_title', 'show_category_on_loop', 1 );
function show_category_on_loop() {
global $product;
$terms = get_the_terms( $product->get_id(), 'product_cat' );
if( $terms ) {
$names = array();
foreach ( $terms as $term ) {
$names[] = $term->name;
}
print '<span class="category-text"> '.join( ', ', $names ).'</span>'.PHP_EOL;
}
}
Now, did you see the “category-text” class over there? This one helps you to customize it with the custom CSS. For example:
/* display Woocommerce product category name above the product title */
.category-text {
font-size: 13px;
margin-bottom: 1em;
}
How to display Woocommerce SKU in the product loop pages?
If you need to add Woocommerce product SKU in the Woocommerce loop pages then just use this snippet here.
add_action( 'woocommerce_after_shop_loop_item_title', 'custom_before_title', 15 );
function custom_before_title() {
global $product;
if ( $product->get_sku() ) {
echo '<span class="sku-text">SKU: ' . $product->get_sku() . '
// display Woocommerce SKU in the product loop pages
add_action( 'woocommerce_after_shop_loop_item_title', 'custom_before_title', 15 );
function custom_before_title() {
global $product;
if ( $product->get_sku() ) {
echo '<span class="sku-text">SKU: ' . $product->get_sku() . '</span>';
}
}
And if you would like to customize the oputput then add this piece of CSS to the customizer.
/* display Woocommerce SKU in the product loop pages */
.sku-text {
font-size: 13px;
margin: 5px 0px;
}
How to display saved amount for Woocommerce discounted products?
This snippet here allows you to display saved amount on your Woocommerce product loop pages. For example: “You save: XX €”. See the screenshot here below.
add_action( 'woocommerce_after_shop_loop_item', 'you_save_for_archives', 1 ); // Change your hook priority
function you_save_for_archives() {
global $product;
// works for Simple and Variable type
$regular_price = get_post_meta( $product->get_id(), '_regular_price', true ); // For example: 49.99
$sale_price = get_post_meta( $product->get_id(), '_sale_price', true ); // For example: 39.99
if( !empty($sale_price) ) {
$saved_amount = $regular_price – $sale_price;
$currency_symbol = get_woocommerce_currency_symbol();
$percentage = round( ( ( $regular_price – $sale_price ) / $regular_price ) * 100 );
?>
<span class="you-save-price">You save: <?php echo $currency_symbol .''. number_format($saved_amount, 2, '.', ''); ?></span>
// Display saved amount for Woocommerce discounted products
add_action( 'woocommerce_after_shop_loop_item', 'you_save_for_archives', 1 ); // Change your hook priority
function you_save_for_archives() {
global $product;
// works for Simple and Variable type
$regular_price = get_post_meta( $product->get_id(), '_regular_price', true ); // For example: 49.99
$sale_price = get_post_meta( $product->get_id(), '_sale_price', true ); // For example: 39.99
if( !empty($sale_price) ) {
$saved_amount = $regular_price - $sale_price;
$currency_symbol = get_woocommerce_currency_symbol();
$percentage = round( ( ( $regular_price - $sale_price ) / $regular_price ) * 100 );
?>
<span class="you-save-price">You save: <?php echo $currency_symbol .''. number_format($saved_amount, 2, '.', ''); ?></span>
<?php
}
}
Also, don’t forget to customize the output with the help of this piece of CSS.
/* display saved amount for Woocommerce discounted products */
.you-save-price {
font-size: 13px;
margin-bottom: 10px;
}
How to display saved amount on Woocommerce single product page?
Just use this code here. It shows the saved amount both for simple and ariable products.
add_action( 'woocommerce_single_product_summary', 'simple_product_saving_amount', 11 );
function simple_product_saving_amount() {
global $product;
if( $product->is_type('simple') && $product->is_on_sale() ) {
$regular_price = (float) wc_get_price_to_display( $product, array('price' => $product->get_regular_price() ) );
$active_price = (float) wc_get_price_to_display( $product, array('price' => $product->get_sale_price() ) );
$saved_amount = $regular_price – $active_price;
$percentage = round( $saved_amount / $regular_price * 100 );
echo '<p id="saving_total_price">'. __("You Save") .': ' . wc_price($saved_amount) . ' ('.$percentage.'%)</p>';
}
}
// For product variations (on variable products)
add_filter( 'woocommerce_available_variation', 'variable_product_saving_amount', 10, 3 );
function variable_product_saving_amount( $data, $product, $variation ) {
if( $variation->is_on_sale() ) {
$saved_amount = $data['display_regular_price'] – $data['display_price'];
$percentage = round( $saved_amount / $data['display_regular_price'] * 100 );
$data['price_html'] .= '<p id="saving_total_price">'. __("You Save") .': ' . wc_price($saved_amount) . ' ('.$percentage.'%)
// For simple products
add_action( 'woocommerce_single_product_summary', 'simple_product_saving_amount', 11 );
function simple_product_saving_amount() {
global $product;
if( $product->is_type('simple') && $product->is_on_sale() ) {
$regular_price = (float) wc_get_price_to_display( $product, array('price' => $product->get_regular_price() ) );
$active_price = (float) wc_get_price_to_display( $product, array('price' => $product->get_sale_price() ) );
$saved_amount = $regular_price - $active_price;
$percentage = round( $saved_amount / $regular_price * 100 );
echo '<p id="saving_total_price">'. __("You Save") .': ' . wc_price($saved_amount) . ' ('.$percentage.'%)</p>';
}
}
// For product variations (on variable products)
add_filter( 'woocommerce_available_variation', 'variable_product_saving_amount', 10, 3 );
function variable_product_saving_amount( $data, $product, $variation ) {
if( $variation->is_on_sale() ) {
$saved_amount = $data['display_regular_price'] - $data['display_price'];
$percentage = round( $saved_amount / $data['display_regular_price'] * 100 );
$data['price_html'] .= '<p id="saving_total_price">'. __("You Save") .': ' . wc_price($saved_amount) . ' ('.$percentage.'%)</p>';
}
return $data;
}
How to display custom text in Woocommerce product loops and single product pages?
Sometimes you may need to have a need to display custom text in Woocommerce product loops and archive pages. Let’s see how can we accomplish that.
First, let’s create a new Woocommerce single product custom field and let’s output it in the desired place.
add_action( 'woocommerce_product_options_general_product_data', 'wpsh_add_text_field' );
function wpsh_add_text_field() {
woocommerce_wp_text_input( array(
'id' => '_shipping_time',
'label' => __( 'Shipping info', 'woocommerce' ),
'description' => __( 'This is a custom field, you can write here anything you want.', 'woocommerce' ),
'desc_tip' => 'true',
'type' => 'text'
) );
}
// Save custom field values
add_action( 'woocommerce_admin_process_product_object', 'wpsh_save_field', 10, 1 );
function wpsh_save_field( $product ) {
if ( isset( $_POST['_shipping_time'] ) ) {
$product->update_meta_data( '_shipping_time', sanitize_text_field( $_POST['_shipping_time'] ) );
}
}
// Display this custom field on Woocommerce single product pages
add_action( 'woocommerce_product_meta_end', 'wpsh_display_on_single_product_page', 10 );
function wpsh_display_on_single_product_page() {
global $product;
// Is a WC product
if ( is_a( $product, 'WC_Product' ) ) {
// Get meta
$text = $product->get_meta( '_shipping_time' );
// NOT empty
if ( ! empty ( $text ) ) {
echo '<div class="woocommerce-message">Estimated shipping time: ' . $text . '</div>';
}
}
}
// Display this custom field on Woocommerce archive pages
add_action( 'woocommerce_after_shop_loop_item', 'wpsh_display_on_archive_page', 10 );
function wpsh_display_on_archive_page() {
global $product;
// Is a WC product
if ( is_a( $product, 'WC_Product' ) ) {
// Get meta
$text = $product->get_meta( '_shipping_time' );
// NOT empty
if ( ! empty ( $text ) ) {
echo '<div class="custom-text">Estimated shipping time: ' . $text . '
// Add custom field to Woocommerce backend under General tab
add_action( 'woocommerce_product_options_general_product_data', 'wpsh_add_text_field' );
function wpsh_add_text_field() {
woocommerce_wp_text_input( array(
'id' => '_shipping_time',
'label' => __( 'Shipping info', 'woocommerce' ),
'description' => __( 'This is a custom field, you can write here anything you want.', 'woocommerce' ),
'desc_tip' => 'true',
'type' => 'text'
) );
}
// Save custom field values
add_action( 'woocommerce_admin_process_product_object', 'wpsh_save_field', 10, 1 );
function wpsh_save_field( $product ) {
if ( isset( $_POST['_shipping_time'] ) ) {
$product->update_meta_data( '_shipping_time', sanitize_text_field( $_POST['_shipping_time'] ) );
}
}
// Display this custom field on Woocommerce single product pages
add_action( 'woocommerce_product_meta_end', 'wpsh_display_on_single_product_page', 10 );
function wpsh_display_on_single_product_page() {
global $product;
// Is a WC product
if ( is_a( $product, 'WC_Product' ) ) {
// Get meta
$text = $product->get_meta( '_shipping_time' );
// NOT empty
if ( ! empty ( $text ) ) {
echo '<div class="woocommerce-message">Estimated shipping time: ' . $text . '</div>';
}
}
}
// Display this custom field on Woocommerce archive pages
add_action( 'woocommerce_after_shop_loop_item', 'wpsh_display_on_archive_page', 10 );
function wpsh_display_on_archive_page() {
global $product;
// Is a WC product
if ( is_a( $product, 'WC_Product' ) ) {
// Get meta
$text = $product->get_meta( '_shipping_time' );
// NOT empty
if ( ! empty ( $text ) ) {
echo '<div class="custom-text">Estimated shipping time: ' . $text . '</div>';
}
}
}