Template overrides
A template is a PHP file with the markup of one page block: the price, the cart, an email. WooCommerce templates live in wp-content/plugins/woocommerce/templates/. When hooks are not enough and the markup itself has to change, the file is copied into the theme and the copy is edited — the original stays untouched.
How WooCommerce looks for a template
Before printing a block the plugin first checks the woocommerce folder in the active theme. If a file with the same path is found, it is used; if not, the plugin takes its own.
Source file in woocommerce/templates/ | Copy in the theme |
|---|---|
single-product/short-description.php | acme-child/woocommerce/single-product/short-description.php |
cart/cart.php | acme-child/woocommerce/cart/cart.php |
emails/customer-processing-order.php | acme-child/woocommerce/emails/customer-processing-order.php |
The word templates is not repeated in the copy's path.
Overriding a template
Copy short-description.php into the child theme — create the woocommerce/single-product folders by hand — and change the markup:
<!-- wp-content/themes/acme-child/woocommerce/single-product/short-description.php -->
<?php
defined('ABSPATH') || exit;
global $post;
$short_description = apply_filters('woocommerce_short_description', $post->post_excerpt);
if (!$short_description) {
return;
}
?>
<div class="woocommerce-product-details__short-description acme-box">
<h3>The product in brief</h3>
<?php echo $short_description; ?>
</div>Open the page of a product with a short description: the heading The product in brief now sits above the text. The real file starts with <?php and a comment holding the version number — leave them as they are.
Your own template in the plugin
The markup of your own blocks is moved into a template too. Create the file templates/single-product/lead-time.php in the plugin:
<!-- wp-content/plugins/acme-shop/templates/single-product/lead-time.php -->
<p class="acme-lead-time">
Made in <strong><?php echo esc_html($days); ?></strong> days
</p>And render it with wc_get_template() instead of the printf() from the previous chapter:
// wp-content/plugins/acme-shop/includes/storefront.php
add_action('woocommerce_single_product_summary', function () {
global $product;
$days = (int) $product->get_meta('_acme_lead_time');
if ($days > 0) {
wc_get_template(
'single-product/lead-time.php',
['days' => $days],
'',
dirname(__DIR__) . '/templates/'
);
}
}, 25);The first argument is the template path, the second the variables available inside it, the fourth the folder where the template lives by default. The product page looks the same, but now the theme can override this file too with a copy at acme-child/woocommerce/single-product/lead-time.php.
Outdated copies
Every WooCommerce template starts with a version number: @version 9.4.0. When the plugin updates a template, the number grows while the copy in the theme stays old. The WooCommerce → Status page, the Templates section, lists such copies: next to the file it shows the theme's version and the plugin's version.
A copy is updated by hand: take the new source file, carry your edits into it and save it over the old one.
When a hook, when a template
| Task | How to solve it |
|---|---|
| Add, remove or move a block | With a hook |
| Change a text or a value | With a filter |
| Change a block's HTML markup | With a template copy |
Common mistake
The whole templates folder is copied into the theme just in case. The shop starts using dozens of frozen files: fixes and new fields from WooCommerce updates never reach them, and the cart or checkout breaks. Copy only the file you actually change.