How to Create a Custom Invoice Template in Magento 2

Why Custom Invoice Templates Matter in Magento 2

If you're running a Magento 2 store, you know that invoices aren't just boring paperwork – they're part of your brand experience. The default invoice template gets the job done, but it looks... well, default. Creating a custom invoice template lets you:

  • Match your brand colors and logo
  • Add custom fields like PO numbers or special instructions
  • Improve readability for your customers
  • Include promotional messages or loyalty program details

The good news? Magento 2 makes this customization surprisingly straightforward once you know where to look. Let's walk through the process step by step.

Understanding Magento 2's Invoice Structure

Before we start coding, it helps to understand how Magento handles invoices:

  1. Layout files define the structure (XML)
  2. Templates handle the HTML/PHP rendering
  3. CSS styles control the appearance

All invoice-related files live in the vendor/magento/module-sales module, but we'll create our own version in our theme to override the defaults.

Step 1: Create Your Custom Theme (If You Haven't Already)

First, make sure you have a custom theme set up. If you're already using one, skip to Step 2.

In your Magento installation:

app/design/frontend/<Vendor>/<Theme>/
├── registration.php
├── theme.xml
└── web/
    ├── css/
    ├── images/
    └── js/

Your registration.php should look like:

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::THEME,
    'frontend/<Vendor>/<Theme>',
    __DIR__
);

And theme.xml:

<theme xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Config/etc/theme.xsd">
    <title>Your Theme Name</title>
    <parent>Magento/blank</parent>
</theme>

Step 2: Override the Invoice Template

Now we'll create our custom invoice template by copying and modifying the default one.

Create this directory structure in your theme:

app/design/frontend/<Vendor>/<Theme>/Magento_Sales/templates/order/email/invoice/

Copy the default items.phtml file from:

vendor/magento/module-sales/view/frontend/templates/order/email/invoice/items.phtml

Paste it into your new directory. This is the file we'll modify to customize our invoice appearance.

Step 3: Customize the Invoice HTML

Open your copied items.phtml file. Here's a simple example of how you might modify it:

<?php
/**
 * Custom invoice template
 */
?>
<table class="email-items" style="width:100%; font-family: Arial, sans-serif;">
    <thead>
        <tr>
            <th class="item-info" style="text-align:left; padding:10px; background:#f5f5f5;">
                <?= $block->escapeHtml(__('Product')) ?>
            </th>
            <th class="item-qty" style="text-align:left; padding:10px; background:#f5f5f5;">
                <?= $block->escapeHtml(__('Qty')) ?>
            </th>
            <th class="item-price" style="text-align:left; padding:10px; background:#f5f5f5;">
                <?= $block->escapeHtml(__('Price')) ?>
            </th>
        </tr>
    </thead>
    <tbody>
        <?php foreach ($_items as $_item): ?>
            <?php if (!$_item->getOrderItem()->getParentItem()) : ?>
                <tr>
                    <td class="item-info" style="padding:10px; border-bottom:1px solid #eee;">
                        <p class="product-name"><?= $block->escapeHtml($_item->getName()) ?></p>
                        <?php if ($_options = $block->getItemOptions($_item)): ?>
                            <dl class="item-options">
                                <?php foreach ($_options as $_option) : ?>
                                    <dt><?= $block->escapeHtml($_option['label']) ?></dt>
                                    <?php if (!$block->getPrintStatus()): ?>
                                        <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?>
                                        <dd>
                                            <?php if (isset($_formatedOptionValue['full_view'])): ?>
                                                <?= $_formatedOptionValue['full_view'] ?>
                                            <?php else: ?>
                                                <?= $_formatedOptionValue['value'] ?>
                                            <?php endif; ?>
                                        </dd>
                                    <?php endif; ?>
                                <?php endforeach; ?>
                            </dl>
                        <?php endif; ?>
                    </td>
                    <td class="item-qty" style="padding:10px; border-bottom:1px solid #eee;">
                        <?= (float)$_item->getQty() ?>
                    </td>
                    <td class="item-price" style="padding:10px; border-bottom:1px solid #eee;">
                        <?= /* @noEscape */ $block->getItemPrice($_item) ?>
                    </td>
                </tr>
            <?php endif; ?>
        <?php endforeach; ?>
    </tbody>
    <tfoot>
        <?= $block->getChildHtml('invoice_totals') ?>
    </tfoot>
</table>

Step 4: Customize the Invoice Layout

To make sure our template is used, we need to override the layout file. Create this file:

app/design/frontend/<Vendor>/<Theme>/Magento_Sales/layout/sales_email_order_invoice_items.xml

With this content:

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="items">
            <action method="setTemplate">
                <argument name="template" xsi:type="string">Magento_Sales::order/email/invoice/items.phtml</argument>
            </action>
        </referenceBlock>
    </body>
</page>

Step 5: Add Custom CSS

For more advanced styling, create a custom CSS file at:

app/design/frontend/<Vendor>/<Theme>/web/css/source/_email-invoice.less

Add your styles there. For example:

.email-invoice {
    .logo {
        text-align: center;
        margin-bottom: 20px;
        
        img {
            max-width: 200px;
        }
    }
    
    .invoice-details {
        background: #f9f9f9;
        padding: 15px;
        margin-bottom: 20px;
        
        .invoice-title {
            color: @primary__color;
            font-size: 18px;
            margin-bottom: 10px;
        }
    }
    
    .order-details {
        margin-bottom: 30px;
    }
}

Step 6: Add Your Company Logo

To add your logo to invoices, go to:

  1. Admin Panel → Stores → Configuration
  2. Sales → Sales Emails
  3. Upload your logo under "Logo for HTML Email"
  4. Set the width to an appropriate size (usually 200px)

Step 7: Adding Custom Fields

Need to add custom fields like PO numbers or special instructions? Here's how:

First, create a plugin to add your custom data to the invoice email. Create this file:

app/code/<Vendor>/<Module>/Plugin/Sales/Order/Invoice.php

With this content:

<?php
namespace <Vendor>\<Module>\Plugin\Sales\Order;

class Invoice
{
    public function beforeGetEmailCustomVariables(
        \Magento\Sales\Model\Order\Invoice $subject,
        $result
    ) {
        $order = $subject->getOrder();
        
        // Add your custom data here
        $result['custom_field'] = $order->getData('your_custom_field');
        
        return [$result];
    }
}

Then reference this data in your template:

<?php if (isset($custom_field) && $custom_field): ?>
    <div class="custom-field">
        <strong><?= __('Custom Field') ?>:</strong>
        <span><?= $custom_field ?></span>
    </div>
<?php endif; ?>

Step 8: Testing Your Custom Invoice

After making all these changes, it's crucial to test:

  1. Create a test order
  2. Generate an invoice for it
  3. Send the invoice email to yourself
  4. Check both HTML and plain text versions

To manually trigger an invoice email from the admin:

  1. Go to Sales → Invoices
  2. Find your test invoice
  3. Click "Send Email"

Advanced Customization: PDF Invoices

If you want your customizations to also appear on PDF invoices, you'll need to override the PDF renderer. Create this file:

app/code/<Vendor>/<Module>/Model/Order/Pdf/Invoice.php

Extend the core class and override the methods you need. The most important one is getPdf() which generates the PDF content.

Common Issues and Troubleshooting

Problem: Changes aren't appearing
Solution: Clear cache and static content: php bin/magento cache:flush and php bin/magento setup:static-content:deploy

Problem: Emails not sending
Solution: Check your email configuration under Stores → Configuration → Sales → Sales Emails

Problem: Layout breaks in email clients
Solution: Stick to simple table-based layouts and inline CSS for maximum compatibility

Final Thoughts

Customizing your Magento 2 invoice templates is a great way to reinforce your brand and improve customer experience. While the process might seem technical at first, breaking it down into these steps makes it manageable.

Remember that invoices are legal documents, so always include all required information. Beyond that, let your brand personality shine through!

Need more advanced customizations? Check out our Magento 2 extensions that can help streamline your invoicing process even further.