Our Top 5 Email Template Hacks
Tags: emailtemplates, css, html
Published: 2018-02-10 22:33:03

---
title: "Our Top 5 Email Template Hacks"
published: true
tags: Email Templates,CSS,HTML
canonical_url: https://www.sparkpost.com/blog/email-template-hacks/
---

Today, I thought I would point out a few of my favorite tricks when creating templates. Let’s call them email template hacks. I’ve briefly mentioned some of these in previous blogs, so today I’ll expand on those ideas and add in some use cases and examples.

### 1. Leveraging Substitution Fields in CSS

The first trick is to use [substitution fields](https://www.sparkpost.com/blog/template-substitutions-white-labeling/) for your CSS values. Colors, fonts, height, pretty much anything can be substituted. Many email clients leverage header styles while others don’t. However you choose to tackle formatting your emails across various clients, you can leverage substitutions both inline, in the header block or both.

There are a few different approaches available if you want to inject CSS in the header block in order to maintain standards through all of your templates. The first method is to simply replace CSS values with substitution data. For example, if modifying the text color for an html \<a\> tag within the \<style\> block I would modify the color field to reference a substitution field. Here’s an example:

```
a {
  text-decoration:none;
  color:{{a_normal_color}};
}
```

Then, in the transformations JSON call, I would have the corresponding substitution field:

```
"a_normal_color" : "#378cd2"
```

This is the easiest way to modify the look and feel without having to rummage through hundreds of template and make changes. This also assumes that you are holding those values somewhere on your server and can retrieve them fairly easily for the transmission JSON creation.

Building this example up, you may use a dynamic\_html and/or dynamic\_text blocks. This allows you to make wholesale changes by bringing in large CSS blocks that you may be storing in a repository of standards. Change the blocks in that repository, and you change all of the templates that get the dynamic html/text referenced.

Please keep in mind that dynamic\_html/text blocks are held within the global substitution\_data blocks, now the recipient substitution\_data blocks. A CSS block within the Transmissions dynamic\_html structure may look something like this:

```
"substitution_data": {
 "dynamic_html": {
"css" : "<style type='text/css'> /* resets + globals */
* { 
	margin:0;
	padding:0;
  	font-family: arial, sans-serif;
}

body {
	-webkit-font-smoothing:antialiased;
	-webkit-text-size-adjust:none;
	width: 100%;
	height: 100%;
}

img { 
	max-width: 100%; 
}

/* general styles */
body {background-color:#fff;}

a {
  text-decoration:none;
  color:#378cd2;	/* blue */
}
a:hover {color:#82b450; /* green */}

/* body styles */
p {
font-family: arial,helvetica,sans-serif; font-size: 1.2rem; line-height: 1.6rem; color: #3e3e3e; font-weight: normal; margin-top: 0px; margin-bottom: 1.8rem; max-width: 500px;
}

li {
font-family: arial,helvetica,sans-serif; font-size: 1.2rem; line-height: 1.4rem; color: #3e3e3e; font-weight: normal; margin-top: 0px; margin-bottom: .5rem;
}

ul {
padding-left:1.3rem; margin-top:0px; margin-bottom:2rem;
}

h3 {
font-family: arial,helvetica,sans-serif; font-size: 1.3rem; line-height: 1.6rem; color: #378cd2; font-weight: bold; margin-top: 1.1rem; margin-bottom: 1.8rem;
}"}}
```

This code block is no different than if you had it placed in the HTML template itself. Now it’s just easier to update when referenced into the template via the transmission which pulls from a central repository. The template itself will use this code block with the following entry in the template somewhere in the html \<header\> block:

```
{{render_dynamic_content(dynamic_html.css)}}
```

If you want to go all out, dynamic rendering even allows for recursive substitution fields. This means that the dynamic code block can have substitution fields as well. A good use case for this is when you OEM or White label your product. In the following example, the CSS block is similar to the one above, but there are substitution fields for the CSS values. Those substitution fields are then placed in the global substitution block of the transmission. For example:

```
"substitution_data": {
"dynamic_html": {
"css" : "<style type='text/css'> /* resets + globals */
* { 
	margin:{{global_margin}};
	padding:{{global_padding}};
  	font-family: {{global_font_family}};
}

body {
	-webkit-font-smoothing:antialiased;
	-webkit-text-size-adjust:none;
	width: {{body_width}};
	height: {{body_height}};
}

img { 
	max-width: {{image_max_width}}; 
}

/* general styles */
body {background-color: {{body_background_color}};}

a {
  text-decoration:none;
  color:{{a_normal_color}};	/* blue */
}
a:hover color:{{a_hover_color}}; /* green */}

/* body styles */
p {
{{p_style}};
}

li {
{{li_style}};
}

ul {
{{ul_style}};
}

h3 {
{{h3_style}};
}"},

"global_margin" : 0,
"global_padding": 0,
"global_font_family" : "arial, sans-serif",
"body_width" : "100%",
"body_height" : "100%",
"image_max_width" : "100%",
"body_background_color" : "#378cd2",
"a_normal_color" : "#378cd2",
"a_hover_color" : "#82b450",
"p_style" : "font-family: arial,helvetica,sans-serif; font-size: 1.2rem; line-height: 1.6rem; color: #3e3e3e; font-weight: normal; margin-top: 0px; margin-bottom: 1.8rem; max-width: 500px",
"li_style" : "font-family: arial,helvetica,sans-serif; font-size: 1.2rem; line-height: 1.4rem; color: #3e3e3e; font-weight: normal; margin-top: 0px; margin-bottom: .5rem",
"ul_style" : "padding-left:1.3rem; margin-top:0px; margin-bottom:2rem",
"h3_style" : "font-family: arial,helvetica,sans-serif; font-size: 1.3rem; line-height: 1.6rem; color: #53e00d; font-weight: bold; margin-top: 1.1rem; margin-bottom: 1.8rem
},
```

But wait, what if I want to use inline CSS, you ask? Well, just change those big blocks of CSS settings and make them substitution fields.

### 2. Not sending the email at all if certain data does not exist

As emails become more personalized, they start to look and feel like transactional emails. This is a great trend, but it opens up a greater possibility for unfinished emails. Let’s say a job board sends lists of job opportunities to all active members each morning. Hopefully there are checks and balances that stop the application from requesting for an email to go out if there are no matches for that user, but as a template designer, I don’t want to rely on that. So a [little trick that I use](https://www.sparkpost.com/blog/consolidate-retail-email-template/) is to check for a specific substitution field or array that must be present; if it doesn’t exist, I call a non-existent function with the following line somewhere in the template (I tend to put this on the bottom). This will automatically kill that email from being generated so the bad email doesn’t make it out the door. In the example below, I’m checking for the ProductList array, and if it doesn’t exist, I call the function ‘crash’ with the parameter ‘onpurpose’.

```
{{if !ProductList}} {{ crash(onpurpose) }}{{end}}
```

### 3. Validating data before creating the table for arrays

SparkPost has a very powerful feature that allows a template to loop through an array in order to display a set of information, like jobs, real estate listings or products. In fact, SparkPost even supports having loops within loops within loops. Often, multiple tables, rows and columns get created order to display this information in the fashion the content creator wishes. But what if there is no data to show in one of those loops, but you still want the email to go out, just without the empty rows or tables? In many of my retail templates, I display a list of products that have ‘x’ number of features for each product. If there are no features, I still want the product to be displayed but I may not need to create some of the corresponding html code for the empty features list. In order to keep the email looking good, I check both the existence and the value of my arrays before building unnecessary tables, rows or columns that would house this information. This trick is solved with a simple two part if statement:

```
{{ if empty(myarray) or !myarray}}
```

### 4. Using substitution fields in the subject line

So you built your template and placed it onto the SparkPost server. Don’t forget to create a dynamic subject line. One of the easiest way’s to get your email clipped by Gmail or buried into a huge thread is to use the same subject line over and over. So go ahead and [use substitution fields](https://www.sparkpost.com/blog/personalize-emails-substitution-data-and-templates/) for your subject. I often put the person’s name, along with a subject field. My Subject field in the SparkPost UI is set to something like:

```
{{first_name}}, {{subject}}
```

### 5. Use dynamic\_html for headers and footers

So, continuing on the theme of standards, I like to send my headers and footers to each template via the Transmission API in the form of a dynamic\_html entry. This allows me to keep a library of headers and footers that can be easily modified without having to change every template. Because SparkPost doesn’t have the ability to store those headers and footers on the server then insert them into the targeted template, it’s a good practice to have those standard headers and footers in another content management system that marketing can change without developments help. When the transmission json is getting built, the appropriate headers and footers are placed into the dynamic\_html section which is then pulled into the template before sending.

### Upcoming Email Template Hacks

So those are some of my favorite template tricks. In my next blog, I’m going to tackle the trick of validating that the transmission payload has all of the data the template is expecting to receive. The blog will be accompanied by a full PHP code sample for this validation step.

Happy Sending!

_This post was originally published on [sparkpost.com](https://www.sparkpost.com/blog/email-template-hacks/)_