-
Notifications
You must be signed in to change notification settings - Fork 109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Render React Component (JSXConsructor) to Email Template #428
Comments
Hello @AlbinoGeek
There are a few options available:
One more thing is that sending the SVG images via email may not be the simplest thing to do due to a bunch of email client limitations, so consider converting them to PNG or another format. |
I found that using I ended up "fixing" this with a convoluted code blob using import Facebook from '@mui/icons-material/Facebook'
import Instagram from '@mui/icons-material/Instagram'
import LinkedIn from '@mui/icons-material/LinkedIn'
import { createReadStream } from 'fs'
import { renderToString } from 'react-dom/server'
import sharp from 'sharp'
const convertSvgToImage = async (
MuiSvgIcon: typeof Facebook
): Promise<Buffer> => {
const html = renderToString(<MuiSvgIcon />)
const css = RegExp(/MuiSvgIcon-root{(.*)}/).exec(html)?.[1] ?? ''
return await sharp(Buffer.from(html
.replace(/<style data-emotion[^>]*>.*<\/style>/, '')
.replace(/class="[^"]*"/, `style="${css}"`)
)).resize(48, 48).png().toBuffer()
}
export const inline = [
{
data: createReadStream(`${__dirname}/../../../../public/logo.png`),
filename: 'logo.png',
contentType: 'image/png',
},
{
data: await convertSvgToImage(Facebook),
filename: 'facebook.png',
contentType: 'image/svg+xml',
},
{
data: await convertSvgToImage(Instagram),
filename: 'instagram.png',
contentType: 'image/svg+xml',
},
{
data: await convertSvgToImage(LinkedIn),
filename: 'linkedin.png',
contentType: 'image/svg+xml',
},
] import { inline as defaultInlines } from '@/server/emails/images'
import FormData from 'form-data'
import Mailgun, {
type FormDataInputValue,
type MailgunMessageData
} from 'mailgun.js'
import type { ReactNode } from 'react'
import { renderToString } from 'react-dom/server'
const mailgun = new Mailgun(FormData)
type Options = Pick<MailgunMessageData,
'attachment' | 'bcc' | 'cc' | 'inline' | 'to'> & {
html?: string
subject: string
text?: string
[key: string]: FormDataInputValue
}
export default async function sendEmail(
data: Options,
component?: ReactNode,
): Promise<boolean> {
if (!process.env.MAILGUN_API_KEY || !process.env.MAILGUN_DOMAIN) {
console.error('MAILGUN_API_KEY and MAILGUN_DOMAIN must be set')
return false
}
// Render component if provided
if (component) data.html = renderToString(component)
if (!data.html && !data.text) {
throw new Error('You must provide either component, html or text')
}
// Append or set inline images
if (data.inline) {
data.inline = [
...defaultInlines,
...data.inline,
]
} else data.inline = defaultInlines
const mg = mailgun.client({
username: 'api',
key: process.env.MAILGUN_API_KEY
})
try {
const res = await mg.messages.create(process.env.MAILGUN_DOMAIN, {
...data as MailgunMessageData,
// Overrides here (such as `from`)
})
if (res.status !== 200) throw new Error(JSON.stringify(res))
} catch (error) {
console.error('Failed to send email', error)
return false
}
return true
} Which resulted in me blindly appending these cid/images to every email, even if they don't include them; which again is far from elegant. The emails load slowly, and some browsers/mail clients show the attachments instead of inlining them as expected. However, the Resendjs emails load faster and include their images as expected, in-place as base64 (seems their server processes the images?) The emails resendjs sends even end up smaller on the client side (165kb .eml vs 191kb with mailgun.) Hopefully this message helps people in the future who are stuck with mailgun due to legacy projects. |
Hello!
What is the expected/supported way to convert a React component to an email template to use with mailgun? It would appear competing solutions are much better suited for this, notably with
Resend.js
having the following:What I've tried so far (with various levels of failure):
Likewise, even if you manually load the images, string replace them in the rendered template with equiv;
cid:
references, many image types (most notably SVG) do not work whatsoever, displaying nosrc
on the receiving side.I'll repeat, with Resendjs, this "just works" including SVG images.
The text was updated successfully, but these errors were encountered: