How to Customize WooCommerce Emails with Code

January 24, 2026
How to Customize WooCommerce Emails with Code

How to Customize WooCommerce Emails with Code

WooCommerce transactional emails (new order, processing, completed, refunded, etc.) are highly visible touchpoints.
The safest way to customize them is to use WooCommerce’s template override system plus targeted hooks/filters—without editing plugin files.

This guide shows three code-first approaches:

  • Use hooks/filters for small, upgrade-safe changes
  • Override email templates in a child theme when you need markup changes
  • Create a custom email notification class when you need a brand-new email type

Choose the Right Customization Level

Level 1: Hooks & Filters

Best for: adding extra content, changing subject/heading, sender details, or injecting styles—without rewriting templates.

Level 2: Template Overrides

Best for: changing email HTML structure (moving blocks, adding columns, rewriting order table layout).
WooCommerce supports overriding templates by copying them into your (child) theme’s /woocommerce/ folder while keeping the same structure. :contentReference[oaicite:0]{index=0}

Level 3: Custom Email Class

Best for: sending a new kind of transactional email (e.g., “Quote approved”, “Documents ready”, “Manual review needed”).

Baseline Safety Rules

  • Never edit WooCommerce core plugin files.
  • Prefer a child theme for overrides so updates don’t overwrite your changes. :contentReference[oaicite:1]{index=1}
  • Scope changes to specific emails where possible (don’t change every email globally unless intentional).
  • Test email output using staging + real mailboxes (Gmail, Outlook, iOS Mail) because HTML email rendering differs.

1) Customize “From” Name and Address

WooCommerce exposes filters for the sender name and address.
These are the simplest high-impact customizations and require no template overrides.

<?php
/**
 * Change WooCommerce email "From" name/address.
 * Put in a small plugin or your child theme's functions.php.
 */

// From name
add_filter( 'woocommerce_email_from_name', function ( $name ) {
  // Example: use store name from WooCommerce settings if present
  $store_name = get_option( 'woocommerce_email_from_name' );
  return $store_name ? $store_name : $name;
}, 10, 1 );

// From address
add_filter( 'woocommerce_email_from_address', function ( $address ) {
  // Example: a dedicated sender address
  return 'no-reply@example.com';
}, 10, 1 );

If you need to change sender details for only one email type, you can conditionally check the current email object
by filtering later in the pipeline (see “Targeting a Specific Email” below).
WooCommerce documents these “from” filters as core hooks. :contentReference[oaicite:2]{index=2}

2) Add Content to Emails Using Hooks

WooCommerce provides action hooks inside email templates so you can insert content without copying template files.
Common insertion points include before/after the order table and inside the footer.
Hook locations are consistent across many transactional emails. :contentReference[oaicite:3]{index=3}

Add a Small Custom Message After the Order Table

<?php
add_action( 'woocommerce_email_after_order_table', function ( $order, $sent_to_admin, $plain_text, $email ) {
  // Only customer emails (not admin notifications)
  if ( $sent_to_admin ) return;

  // Only specific email IDs (example: customer_processing_order)
  if ( empty( $email ) || ! is_object( $email ) || empty( $email->id ) ) return;
  if ( $email->id !== 'customer_processing_order' ) return;

  // Keep HTML email-safe: simple tags, inline-friendly.
  echo '<p>Need help? Reply to this email or contact support via your account page.</p>';
}, 10, 4 );

Plain Text Compatibility

If you support plain text emails, respect the $plain_text flag:

<?php
add_action( 'woocommerce_email_after_order_table', function ( $order, $sent_to_admin, $plain_text, $email ) {
  if ( $sent_to_admin ) return;
  if ( empty( $email->id ) || $email->id !== 'customer_completed_order' ) return;

  if ( $plain_text ) {
    echo "\n" . 'Download your invoice from: ' . esc_url( wc_get_endpoint_url( 'orders', '', wc_get_page_permalink( 'myaccount' ) ) ) . "\n";
    return;
  }

  echo '<p>You can download your invoice from your account page.</p>';
}, 10, 4 );

3) Change Email Subject and Heading

Most WooCommerce emails have filters for subject and heading.
These are ideal when you want clearer copy without rewriting templates.

<?php
// Subject: Customer "Processing order"
add_filter( 'woocommerce_email_subject_customer_processing_order', function ( $subject, $order ) {
  if ( ! $order instanceof WC_Order ) return $subject;

  $order_number = $order->get_order_number();
  return sprintf( 'We received your order #%s', $order_number );
}, 10, 2 );

// Heading: Customer "Processing order"
add_filter( 'woocommerce_email_heading_customer_processing_order', function ( $heading, $order ) {
  return 'Order confirmed';
}, 10, 2 );

4) Customize Email Styles Without Editing Templates

WooCommerce emails typically compile CSS into the email header and inline styles for compatibility.
You can filter the generated styles to add small changes (colors, spacing, typography).

<?php
add_filter( 'woocommerce_email_styles', function ( $css, $email ) {
  // Scope to a specific email if desired
  if ( is_object( $email ) && ! empty( $email->id ) && $email->id !== 'customer_completed_order' ) {
    return $css;
  }

  $css .= "\n" . 'a { text-decoration: none; }';
  $css .= "\n" . '.footer { font-size: 12px; }';

  return $css;
}, 10, 2 );

This keeps your changes upgrade-safe and avoids copying the entire email template set.

5) Override Email Templates (When You Need Markup Changes)

When hooks aren’t enough, override the specific template file.
WooCommerce supports template overrides by copying files into your-child-theme/woocommerce/
while preserving the internal directory structure. :contentReference[oaicite:4]{index=4}

Common Email Template Paths

  • woocommerce/templates/emails/email-header.php
  • woocommerce/templates/emails/email-footer.php
  • woocommerce/templates/emails/customer-processing-order.php
  • woocommerce/templates/emails/email-order-details.php

Example override location in your child theme:

  • your-child-theme/woocommerce/emails/email-header.php

Minimal Example: Add a Preheader Line (Header Template Override)

<?php
/**
 * File: your-child-theme/woocommerce/emails/email-header.php
 * Copy from WooCommerce and modify carefully.
 */

defined( 'ABSPATH' ) || exit;

// Keep WooCommerce variables intact: $email_heading, etc.
?>
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=<?php bloginfo( 'charset' ); ?>" />
  <title><?php echo esc_html( get_bloginfo( 'name', 'display' ) ); ?></title>
</head>
<body>

<p style="margin:0 0 12px; font-size:12px; color:#666;">
  This is an automated message about your order.
</p>

<h2><?php echo esc_html( $email_heading ); ?></h2>

Only override what you must. Copying many templates increases maintenance because WooCommerce may update templates over time. :contentReference[oaicite:5]{index=5}

6) Add a Brand-New WooCommerce Email Type (Custom Class)

If you need a custom notification that behaves like native WooCommerce emails,
register a new email class and trigger it using hooks (e.g., on order status change).
This is more work, but it scales better than hacking templates for non-standard flows.

Register the Email Class

<?php
/**
 * Register a custom WooCommerce email class.
 * Put in a small plugin (recommended) so it is theme-independent.
 */

add_filter( 'woocommerce_email_classes', function ( $emails ) {
  if ( ! class_exists( 'WC_Email' ) ) return $emails;

  require_once __DIR__ . '/class-wc-email-documents-ready.php';
  $emails['WC_Email_Documents_Ready'] = new WC_Email_Documents_Ready();

  return $emails;
} );

Trigger the Email

<?php
add_action( 'woocommerce_order_status_completed', function ( $order_id ) {
  $mailer = WC()->mailer();
  $emails = $mailer->get_emails();

  if ( empty( $emails['WC_Email_Documents_Ready'] ) ) return;

  /** @var WC_Email $email */
  $email = $emails['WC_Email_Documents_Ready'];
  $email->trigger( $order_id );
} );

This pattern keeps you aligned with WooCommerce’s email pipeline and settings UI.

Targeting a Specific Email (The Practical Technique)

Whenever you hook into a generic email action (like woocommerce_email_after_order_table),
use the $email->id check. This prevents accidental global changes.

<?php
function wpct_is_email( $email, $id ): bool {
  return is_object( $email ) && ! empty( $email->id ) && $email->id === $id;
}

Testing & Troubleshooting

  • Use a staging site and real orders to generate emails.
  • Use a mailbox that shows raw headers to verify “From” and reply-to behavior.
  • When markup looks broken in one client, simplify: fewer wrappers, more inline-friendly HTML.
  • If you override templates, periodically compare your overrides with updated WooCommerce templates.

Note: WooCommerce Email Editor (Block Email Editor) and Code Customization

WooCommerce has been introducing an Email Editor feature (Block Email Editor) that extensions can integrate with.
As of early 2026, this feature is described as alpha in WooCommerce developer documentation. :contentReference[oaicite:6]{index=6}

Even with an editor UI, code-based hooks and targeted template overrides remain important for:

  • Conditional logic (per product, per role, per customer segment)
  • Injecting dynamic content from custom fields
  • Maintaining strict consistency across transactional templates

Summary

  • Use hooks/filters for most email changes (content, subject, sender, styles).
  • Override templates only when you truly need HTML structure changes. :contentReference[oaicite:7]{index=7}
  • Scope everything to specific email IDs to avoid global side effects.
  • For custom workflows, register a new email class and trigger it explicitly.
  • Test across email clients and keep overrides minimal for long-term maintainability.

If you want, tell me which exact email(s) you’re customizing (e.g., processing, completed, admin new order),
and what change you need (layout vs content vs conditional logic), and I’ll produce a minimal, production-safe snippet set.

Avatar

Written by

satoshi

I’ve been building and customizing WordPress themes for over 10 years. In my free time, you’ll probably find me enjoying a good football match.