Embedding multiple images directly in HTML email content using Mailgun and Node.js

I’m working with Mailgun’s API to send HTML emails and running into an issue with image display. Here’s my current implementation:

const mailgunJS = require('mailgun-js');
const API_DOMAIN = 'sandbox123456.mailgun.org';
const mailClient = mailgunJS({apiKey: 'key-abc123def456', domain: API_DOMAIN});
const imagePath = path.resolve(__dirname, 'images/logo.jpg');

const emailData = {
    from: 'MyApp <[email protected]>',
    to: '[email protected]',
    subject: 'Welcome Email',
    html: '<div><img src="cid:logo.jpg"/><p>Welcome to our service!</p></div>',
    inline: imagePath
};

mailClient.messages().send(emailData, (err, response) => {
    if (err) console.error(err);
    console.log(response);
});

The problem is that my image shows up as a downloadable attachment instead of being embedded in the email body. I also need to include several images within my HTML template. Has anyone successfully embedded multiple inline images in Mailgun emails? What’s the correct approach for this?

Had this exact problem a few months ago. The issue was how I structured the attachment parameter. Using the inline property as an array fixed it for multiple images:

const emailData = {
    from: 'MyApp <[email protected]>',
    to: '[email protected]',
    subject: 'Welcome Email',
    html: '<div><img src="cid:logo"/><img src="cid:banner"/><p>Welcome!</p></div>',
    inline: [
        {
            data: fs.readFileSync('./images/logo.jpg'),
            filename: 'logo.jpg',
            cid: 'logo'
        },
        {
            data: fs.readFileSync('./images/banner.png'),
            filename: 'banner.png', 
            cid: 'banner'
        }
    ]
};

Use inline as an array of objects instead of a single file path. Each object needs the data buffer, filename, and the cid that matches your HTML references. Works reliably across different email clients including Outlook and Gmail.

This issue arises when the Content-ID header is not set correctly. To ensure that Mailgun correctly embeds the images, you must define the Content-ID explicitly using the attachment parameter with the inline disposition. Here’s a corrected version of your code:

const emailData = {
    from: 'MyApp <[email protected]>',
    to: '[email protected]',
    subject: 'Welcome Email',
    html: '<div><img src="cid:logo"/><p>Welcome to our service!</p></div>',
    attachment: new mailClient.Attachment({
        data: fs.readFileSync(imagePath),
        filename: 'logo.jpg',
        contentType: 'image/jpeg',
        cid: 'logo'
    })
};

Ensure that the CID in your HTML matches exactly with what is defined in the attachment object. For embedding multiple images, you can include an array of attachment objects, each with unique CID values. This should work consistently across various email clients when using Mailgun’s Node.js library.

Try FormData instead. I had the same problem and switching to the FormData constructor fixed it for me. Don’t include file extensions in your cid references - use ‘logo’ instead of ‘logo.jpg’ in the HTML src. Also check your Mailgun domain settings. Inline attachments sometimes get blocked when SPF records aren’t set up right.