Recently, I published “How to build a custom form in CraftCMS without plugin“. In that post, the email is sent out with inline HTML body content. In this post, we will change from using HTML body content to using the email template.
Email template
Here are a few ideas to create your own email template.
- You can use a tool like MailChimp or other campaign marketing tools for building your custom email template and export the HTML file then convert it to a Twig template for CraftCMS.
- Another option, you can use the universal email template from the Universal email template for CraftCMS.
- Using MJML which is a helping tool to create a responsive email.
Email HTML boilerplate
I create my custom email template in “templates>_email>_main-layout.twig“. You can create your own email template and store it anywhere under the “templates” folder in CraftCMS. The _mail-layout.twig will be my base email HTML boilerplate for the whole website.
For the email templates, I mainly use the table-based layouts. Yes, it is an old style but it works well with email templates.
In _main-layout.twig, we will add the block section which allows you to override the piece of the section we want.
Here is the sample of _main-layout.twig
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org=/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="{{ craft.app.language }}">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
<!--[if !mso]><!-->
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<!--<![endif]-->
<!--[if (gte mso 9)|(IE)]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<!--[if (gte mso 9)|(IE)]>
<style type="text/css">
body {width: 600px;margin: 0 auto;}
table {border-collapse: collapse;}
table, td {mso-table-lspace: 0pt;mso-table-rspace: 0pt;}
img {-ms-interpolation-mode: bicubic;}
</style>
<![endif]-->
<style type="text/css">
body, p, div {
font-family: verdana,geneva,sans-serif;
font-size: 16px;
}
body {
color: #516775;
}
body a {
color: #993300;
text-decoration: none;
}
p { margin: 0; padding: 0; }
table.wrapper {
width:100% !important;
table-layout: fixed;
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: 100%;
-moz-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
img.max-width {
max-width: 100% !important;
}
.column.of-2 {
width: 50%;
}
.column.of-3 {
width: 33.333%;
}
.column.of-4 {
width: 25%;
}
@media screen and (max-width:480px) {
.preheader .rightColumnContent,
.footer .rightColumnContent {
text-align: left !important;
}
.preheader .rightColumnContent div,
.preheader .rightColumnContent span,
.footer .rightColumnContent div,
.footer .rightColumnContent span {
text-align: left !important;
}
.preheader .rightColumnContent,
.preheader .leftColumnContent {
font-size: 80% !important;
padding: 5px 0;
}
table.wrapper-mobile {
width: 100% !important;
table-layout: fixed;
}
img.max-width {
height: auto !important;
max-width: 100% !important;
}
a.bulletproof-button {
display: block !important;
width: auto !important;
font-size: 80%;
padding-left: 0 !important;
padding-right: 0 !important;
}
.columns {
width: 100% !important;
}
.column {
display: block !important;
width: 100% !important;
padding-left: 0 !important;
padding-right: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
}
.social-icon-column {
display: inline-block !important;
}
}
</style>
<!--user entered Head Start-->
<!--End Head user entered-->
</head>
<body>
<center class="wrapper" data-link-color="#993300" data-body-style="font-size:16px; font-family:verdana,geneva,sans-serif; color:#516775; background-color:#F9F5F2;">
<div class="webkit">
<table cellpadding="0" cellspacing="0" border="0" width="100%" class="wrapper" bgcolor="#F9F5F2">
<tr>
<td valign="top" bgcolor="#F9F5F2" width="100%">
<table width="100%" role="content-container" class="outer" align="center" cellpadding="0" cellspacing="0" border="0">
<tr>
<td width="100%">
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td>
<!--[if mso]>
<center>
<table>
<tr>
<td width="600">
<![endif]-->
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="width:100%; max-width:600px;" align="center">
<tr>
<td role="modules-container" style="padding:0px 0px 0px 0px; color:#516775; text-align:left;" bgcolor="#F9F5F2" width="100%" align="left">
<table class="wrapper" role="module" data-type="image"
border="0" cellpadding="0" cellspacing="0" width="100%"
style="table-layout: fixed;"
data-muid="qa8oMphYHuL7xyQrTVscgD">
<tbody>
<tr>
<td style="font-size:6px; line-height:10px; padding:30px 0px 0px 0px;"
valign="top" align="center">
<img class="max-width" border="0"
style="display:block; color:#000000; text-decoration:none; font-family:Helvetica, arial, sans-serif; font-size:16px; max-width:100% !important; width:100%; height:auto !important;"
src="{{ siteUrl }}img/logo.png"
alt="Apple Rinquest" width="600"
data-responsive="true"
data-proportionally-constrained="false"></td>
</tr>
</tbody>
</table>
<table class="module" role="module" data-type="text" border="0" cellpadding="0" cellspacing="0" width="100%" style="table-layout: fixed;"
data-muid="e3bf1e0d-66a3-4fe0-b559-88c35e958022"
data-mc-module-version="2019-10-22">
<tbody>
<tr>
<td style="padding:18px 0px 18px 0px; line-height:22px; text-align:inherit; background-color:#FFFFFF;"
height="100%" valign="top" bgcolor="#FFFFFF"
role="module-content">
{% block content %}
{% endblock %}
</td>
</tr>
</tbody>
</table>
<table class="module" role="module" data-type="spacer" border="0" cellpadding="0" cellspacing="0" width="100%" style="table-layout: fixed;"
data-muid="f5F8P1n4pQyU8o7DNMMEyW">
<tbody>
<tr>
<td style="padding:0px 0px 30px 0px;"
role="module-content" bgcolor="">
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</center>
</body>
</html>
We add the content block which we will change the content based on what email purpose. For example, if we send the notification appointment email, we will override the content block by appointment info.
Appointment email template
Refer to “How to build a custom form in CraftCMS without plugin“. In that post, I create the appointment form and then I send the notification email after submitting the form. In this post, I will create the appointment email template that inherits from the _main-layout.twig. To do that, I create a new new-appointment.twig under “templates>_emails” in CraftCMS. Below is the code in the template.
{# Base Layout #}
{% extends "_email/_main-layout.twig" %}
{% block content %}
<div>
<div style="font-family: inherit; text-align: left">
<table>
<tr><td>{{ 'Appointment date'|t }}</td><td> {{ submission.appointmentDate }}</td></tr>
<tr><td>{{ 'Appointment time'|t }}</td><td>{{ submission.appointmentTime }}</td></tr>
<tr><td>{{ 'Name'|t }}</td><td>{{ submission.fromName }}</td></tr>
<tr><td>{{ 'Phone number'|t }}</td><td>{{ submission.fromTel }}</td></tr>
<tr><td>{{ 'E-mail address'|t }}</td><td>{{ submission.fromEmail }}</td></tr>
<tr><td>{{ 'Preferred contact method'|t }}</td><td>{{ submission.contactChoice }}</td></tr>
</table>
</div>
</div>
{% endblock %}
Basically, we extend the main layout and override the content block.
Sending email with email template
Refer to “How to build a custom form in CraftCMS without plugin“, we will send the email message with our email template by using “craft\web\View” class as well.
use craft\web\View;
...
// https://docs.craftcms.com/api/v3/craft-mail-message.html
// Message class represents an email message.
$message = new Message();
// Recipients
$message->setFrom([$settings['fromEmail'] => $settings['fromName']]); // Add a recipient, Name is optional
$message->setTo([
$settings['fromEmail'] => $settings['fromName'],
$submission->fromEmail => $submission->fromName
]);
$message->setReplyTo([$submission->fromEmail => $submission->fromName]);
// $message->setCc([$submission->fromEmail => $submission->fromName]);
// $message->setBcc([$settings['fromEmail'] => $settings['fromName']]);
// Content
$message->setSubject($submission->subject);
$contentTextBody = '
<ul>
<li><b>'. Craft::t('site','Appointment date') . ': </b>' . $submission->appointmentDate .'</li>
<li><b>'. Craft::t('site','Appointment time') . ': </b>' . $submission->appointmentTime .'</li>
<li><b>'. Craft::t('site','Name') . ': </b>' . $submission->fromName .'</li>
<li><b>'. Craft::t('site','Phone number') . ': </b>' . $submission->fromTel .'</li>
<li><b>'. Craft::t('site','E-mail address') . ': </b>' . $submission->fromEmail .'</li>
<li><b>'. Craft::t('site','Preferred contact method') . ': </b>' . $submission->contactChoice .'</li>
</ul>
<p>'.$settings['fromName'].'</p>
';
// $data will be passed to the email template.
$data = [
'submission' => $submission
];
$message->setHtmlBody(Craft::$app->getView()->renderTemplate('_email/new-appointment', $data));
$message->setTextBody($contentTextBody);
....
From the code above, at the $message->setHtmlBody(), we pass the appointment template and form data using renderTemplate().
Because we pass the data to the template, we can refer to the data in the template just like we do in the normal twig template in CraftCMS. Below is the appointment template, we can just call the submission JSON object which we pass from renderTemplate().
{% block content %}
<div>
<div style="font-family: inherit; text-align: left">
<table>
<tr><td>{{ 'Appointment date'|t }}</td><td> {{ submission.appointmentDate }}</td></tr>
<tr><td>{{ 'Appointment time'|t }}</td><td>{{ submission.appointmentTime }}</td></tr>
<tr><td>{{ 'Name'|t }}</td><td>{{ submission.fromName }}</td></tr>
<tr><td>{{ 'Phone number'|t }}</td><td>{{ submission.fromTel }}</td></tr>
<tr><td>{{ 'E-mail address'|t }}</td><td>{{ submission.fromEmail }}</td></tr>
<tr><td>{{ 'Preferred contact method'|t }}</td><td>{{ submission.contactChoice }}</td></tr>
</table>
</div>
</div>
{% endblock %}
And that’s how we send the email with the email template in CraftCMS.
If this post is helpful and saves you time, please consider buying me a coffee. It will make me smile 🙂