WooCommerce includes a complete email notification system built on top of the WC_Email base class. Every transactional email — new order notification, order processing, order complete, customer invoice — is a PHP class that extends WC_Email and defines the trigger, subject, heading, and template. Creating a custom email means extending the same class, which gives you the full WooCommerce email infrastructure: the branded HTML wrapper template, variable substitution, the plain-text version, and the admin customisation panel in WooCommerce → Settings → Emails where store owners can customise subject and heading without code. The most common use case is sending a custom notification when an order reaches a specific custom status, or including additional information in an existing email. To add a custom email to WooCommerce, you filter woocommerce_email_classes to register your class and hook your trigger method onto the relevant action — typically an woocommerce_order_status_{old}_to_{new} hook. The email template is an HTML file placed in woocommerce/emails/ inside your theme or plugin, following the WooCommerce template structure. The WC_Email base class handles sending via wp_mail(), applies the store’s email header and footer templates, and provides helper methods like format_string() for placeholder substitution. Combine this with the custom order status guide and the checkout fields guide to build a complete custom order workflow.
Problem: You need to send a custom styled transactional email to the customer when an order reaches a specific status, using the WooCommerce email template system rather than raw wp_mail().
Solution: Extend WC_Email, register the class via woocommerce_email_classes, and trigger it on the target order status hook:
// Register custom email class
add_filter( 'woocommerce_email_classes', 'ha_register_custom_email' );
function ha_register_custom_email( $email_classes ) {
require_once get_stylesheet_directory() . '/inc/class-ha-ready-to-ship-email.php';
$email_classes['HA_Ready_To_Ship_Email'] = new HA_Ready_To_Ship_Email();
return $email_classes;
}
// inc/class-ha-ready-to-ship-email.php
if ( ! defined( 'ABSPATH' ) ) exit;
class HA_Ready_To_Ship_Email extends WC_Email {
public function __construct() {
$this->id = 'ha_ready_to_ship';
$this->customer_email = true; // Send to customer
$this->title = 'Ready to Ship';
$this->description = 'Sent when an order moves to the "Ready to Ship" status.';
$this->subject = 'Your order #{order_number} is ready to ship!';
$this->heading = 'Great news — your order is ready!';
// Template path inside theme: woocommerce/emails/ha-ready-to-ship.php
$this->template_html = 'emails/ha-ready-to-ship.php';
$this->template_plain = 'emails/plain/ha-ready-to-ship.php';
// Trigger on order status change to 'ready-to-ship'
add_action( 'woocommerce_order_status_processing_to_ready-to-ship_notification', [ $this, 'trigger' ], 10, 2 );
parent::__construct();
}
public function trigger( $order_id, $order = false ) {
$this->setup_locale();
$order = $order instanceof WC_Order ? $order : wc_get_order( $order_id );
if ( ! $order ) return;
$this->object = $order;
$this->recipient = $this->object->get_billing_email();
$this->placeholders['{order_number}'] = $order->get_order_number();
$this->placeholders['{order_date}'] = wc_format_datetime( $order->get_date_created() );
if ( $this->is_enabled() && $this->get_recipient() ) {
$this->send(
$this->get_recipient(),
$this->get_subject(),
$this->get_content(),
$this->get_headers(),
$this->get_attachments()
);
}
$this->restore_locale();
}
public function get_content_html() {
return wc_get_template_html(
$this->template_html,
[ 'order' => $this->object, 'email_heading' => $this->get_heading(), 'email' => $this ],
'',
$this->template_base
);
}
public function get_content_plain() {
return wc_get_template_html(
$this->template_plain,
[ 'order' => $this->object, 'email_heading' => $this->get_heading(), 'email' => $this ],
'',
$this->template_base
);
}
}
NOTE: The email template file at woocommerce/emails/ha-ready-to-ship.php in your theme must start with <?php do_action( ‘woocommerce_email_header’, $email_heading, $email ); ?> and end with <?php do_action( ‘woocommerce_email_footer’, $email ); ?> to include the WooCommerce branded header and footer. After registering the class, go to WooCommerce → Settings → Emails to confirm the email appears in the list — you can preview and customise the subject and heading there without touching code.