I just launched a new course on ES6! Use the code WESBOS for $10 off.

Strengthen your core JavaScript skills and master all that ES6 has to offer. Start Now →

Easy Creation of HTML with JavaScript’s Template Strings

Another feature of template literals or template strings is the ability have multi-line strings without any funny business.

Previously with regular string we would have escape the new lines like so:

var text = "hello there, \
 how are you + \
  ";

That gets to be quite a task, to be able to do that. In ES6, we can now use template strings. I like to use these multiline strings when I’m creating HTML markup as a string. You can use backticks here, and use the object properties as variables. These look familiar because they’re in the same format as I’ve been using in some of the previous posts. Let’s define an object and I can show you what I mean:

const person = {
    name: 'Wes',
    job: 'Web Developer',
    city: 'Hamilton',
    bio: 'Wes is a really cool guy that loves to teach web development!'
}

// And then create our markup:
const markup = `
 <div class="person">
    <h2>
        ${person.name}
    </h2>
    <p class="location">${person.location}</p>
    <p class="bio">${person.bio}</p>
 </div>
`;

You can see how this is so much nicer to look at. You will get the whitespace included in the string, but since we’re just creating HTML markup, it doesn’t really matter.

Now, I can take this string and go ahead and dump it into an existing element in an HTML page. Just for sake of an example, you can use a blank page where the only existing element that we have on the page is the document.body, and then we can set our markup variable as the inner.HTML:

const markup = `
 <div class="person">
    <h2>
        ${person.name}
    </h2>
    <p class="location">${person.location}</p>
    <p class="bio">${person.bio}</p>
 </div>
`;

document.body.innerHTML = markup;

You could also use document.createElement to create an element, set the inner.HTML, and then append that to the body, whatever you like, which is the same with markup.

When we refresh the page you see "Wes, web developer, Hamilton, really cool guy," and so on, all on their own individual lines.

If you inspect that, you’ll see it’s all been processed as proper HTML without having to do any document.createElement.

If you console.log the markup just to show you that the new lines are there. You can see that all of the new lines, all of the tabs and the spaces are included as part of that string.

Note: – It’s important to note that any time you are creating HTML and the data comes from the user, we need to escape that data. Much more on this in future post but for now let’s assume this data is clean.

Looping with Template Strings

Another amazing feature of template strings is that you can nest them inside of each other. What if I have an array of dogs and I want to loop over and get myself a list item for each one?

const dogs = [
    { name: 'Snickers', age: 2 },
    { name: 'Hugo', age: 8 },
    { name: 'Sunny', age: 1 }
];

Let’s use a template string, and create an unordered list with the class of dogs, and inside of that I want a list item for each one. But I can’t, I can’t just do something like ${dogs[0].name} because that would be cheating and that’s not really scalable. So how would I loop over every single one?

We can nest template strings right inside of it. How do we do that? Let’s take a look here:

const markup = `
<ul class="dogs">
    ${dogs.map(dog => `<li>${dog.name} is ${dog.age * 7}</li>`)}
</ul>
`;

Here we are using a template string inside of a template string, so we’re going to return a list item, inside of that actual list item we are going to use ${dog.name} and we’re going to say how old they are in dog years, which is ${dog.age *7}.

Now we’ve got all this markup here. Let’s use console.log, and see where we’re at. You should see:

<ul class="dogs">
    <li>Snickers is 14</li>,<li>Hugo is 56</li>,<li>Sunny is 7</li>
</ul>

But we have that comma in there, so how do you get rid of that? We know that map will return an array, so we can simply just use join and an empty string, which will join it without the commas:

const markup = `
<ul class="dogs">
    ${dogs.map(dog => <li>${dog.name} is ${dog.age * 7}</li>`).join('')}
</ul>
`;

If you want to test this, you can use document.body.innerHTML = markup to put it right onto the page for us.

Again, you could do this on their own lines if you prefer to do each on their own lines and indent it, which is just much more maintainable:

const markup = `
<ul class="dogs">
    ${dogs.map(dog => 
            `<li>${dog.name}
            is 
            ${dog.age * 7}
            </li>`
     ).join('')}
 </ul>
`;

Conditional If Statements with Template Strings

Let’s look at an example where we need an if statement inside of our template string. This is taken straight from how you do if statements inside of a React render template, and that is with a ternary operator.

Here I’ve got a song, some data with a name and an artist:

const song = {
    name: 'Dying to live',
    artist: 'Tupac',
    featuring: 'Biggie Smalls'
};

We always have a name of the song and the artist of the song, but sometimes we’ve got a featuring artist. If there is a featuring, we need to include it in our markup so we can add it to our document.body.innerHTML like this:

const markup = `
    <div class="song">
        <p>
            ${song.name} - ${song.artist}
            (Featuring ${song.featuring})
        </p>
    </div>
`;

document.body.innerHTML = markup;

If you load that, you’ll see `Dying to Live – Tupac (Featuring Biggie Smalls).

That works, but what if we delete Biggie Smalls, we get `(Featuring undefined).

If it’s not there, we don’t want the parentheses or the word "Featuring," or anything there. A way we can get around that is by using a ternary operator. Our ternary operator will say if "this" then "that", otherwise nothing:

const markup = `
    <div class="song">
        <p>
            ${song.name} - ${song.artist}
            ${song.featuring ? `(Featuring ${song.featuring})` : ''}
        </p>
    </div>
`;

document.body.innerHTML = markup;

Using the ternary operator, we can use that to add any featured artist if there is one, otherwise it will just use a blank string, which will remove the featured artist brackets.

There we have it – a nice little way to do an if statement right inside template strings.

Template Strings Render Functions

The first few examples were pretty simple, but what happens when your data starts to get a little bit complex?

With nesting inside of nesting, inside of nesting, it starts to get a little bit hairy and harder to maintain your code. What I like to do is create what I call a render function. I’ve sort of taken that from React, where we create separate components that will handle different complex data and different components in our markup.

const beer = {
    name: 'Belgian Wit',
    brewery: `Steam Whistle Brewery`,
    keywords: ['pale', 'cloudy', 'spiced', 'crisp']
};

const markup = `
<div class="beer">
    <h2>${beer.name}</h2>
    <p class="brewery">${beer.brewery}</p>
</div>
`;

document.body.innerHTML = markup;

Just like you’d expect, in your HTML you’ll get the beer name and the beer brewery, and that’s all wrapped appropriately in <h2> and a <p> tags, and it’s looking pretty good. But what if I want to implement our array of keywords that’s nested inside of the actual beer object? I could just go ahead and do map right on one line, but I’d rather kick it off to a separate function, which I’ll call renderKeywords:

const beer = {
    name: 'Belgian Wit',
    brewery: `Steam Whistle Brewery`,
    keywords: ['pale', 'cloudy', 'spiced', 'crisp']
};

function renderKeywords(keywords) {
    return `
    <ul>
        ${keywords.map(keyword => `<li>${keyword}</li>`)}
    </ul>
    `;
}

const markup = `
<div class="beer">
    <h2>${beer.name}</h2>
    <p class="brewery">${beer.brewery}</p>
    ${renderKeywords(beer.keywords).join('')}
</div>
`;

document.body.innerHTML = markup;

You can see that our function an unordered list, and then uses the map function to fill in the keywords from our array as list items. It’s going to give us the keyword, and that is going to return, again, another template string that has the key word inside of it. We’re also using the join function to make sure that our keywords don’t include the commas from the array.

Now this function should just be able to pass it off. It’s only one line, and it should be able to create the unordered list, and the list item, any other HTML that we need to have created inside of this.

If you take a look at our HTML you’ve got your unordered list with all of the list items inside, and you can see that any time you need to render out a unordered list of keywords, whether it’s tied to this particular beer or not, it can simply just use renderKeywords to get the markup it needs.

This entry was posted in ES6, JavaScript. Bookmark the permalink.

14 Responses to Easy Creation of HTML with JavaScript’s Template Strings

  1. Hnas B PUFAL says:

    Instead of the ternary operator :

    ${song.featuring ? `(Featuring ${song.featuring})` : ”}

    use

    ${song.featuring || ”}

  2. Nathan says:

    Wouldn’t your `renderKeywords()` function need to do the `.join()` immediately after the `.map()`? You can’t call `.join()` on the string returned by the function.

  3. Sivan says:

    I think `.join(”)` in the last example should be after `keywords.map()` in the **renderKeywords** function 😛

  4. Yevgeny says:

    Thanks for sharing your knowledge.

    In the last code snippet join() method doesn’t work, since it’s invoked with a string, not an array.

    Here, I’ve refactored renderKeywords a bit function:

    function renderKeywords(keywords, delimiter=”) {
    return `

    ${keywords.map(keyword => `${keyword}`).join(delimiter)}

    `;
    }

    Then .join() method can be removed from markup const altogether:

    const markup = `

    ${beer.name}
    ${beer.brewery}
    ${renderKeywords(beer.keywords)}

    `;

  5. Igor says:

    Great post, thanks for sharing!
    I think there is a mistake in the first example with Person object. You used a ‘city’ key, but then in the string you are referencing ‘location’, which ends up as undefined.
    Also, in the last example, would it be better to move the join(”) inside the function to keep the markup cleaner?

  6. Goutham says:

    How can we call a function on a tag in template strings?

  7. Olivier Krull says:

    This is exactly how we are building our whole applications. Since firefox won’t support html imports, we’re able to import easily our custom elements which are implemented with the same pattern only with js, some css and without polyfill.

    With a differ we re-render (with the mentioned render function) the needed DOM parts.

    Everything bundled together (browserify), results to a very fast loading application.

  8. AliMalik says:

    Join is not working where you have placed it in const markup as it is for arrays.

    In order to get it working, I have placed it inside renderKeywords function as:

    function renderKeywords(keywords) {
    return `

    ${keywords.map(keyword => `${keyword}`).join(”)}

    `;
    }

  9. Abhishek Ghosh says:

    Hey Wes! Really nice article. I had a small question. How do we render React components within Template Strings ?

    I am using a component which renders stars based on the input rating. Now, I also want to render this ratings component within a template string, but all I see is `[object Object]`. Is there a way to achieve this ?

    const ratingStars = place.rating
    ?
    : ‘unknown’;
    const content = `

    ${place.name}

    Rating: ${ratingStars}

    Price: ${place.price_level ? place.price_level + ‘/5’ : ‘unknown’}

    `;
    infowindow.setContent(content);
    infowindow.open(map, marker);

    NOTE: I render this in the InfoWindow of a Google Maps’ marker.

    Thank You for your help!

  10. Marco says:

    Nice. Helped a lot. BTW, nice colors.

  11. There’s not much better than a well authored article!
    Thank you so much for this relief, I relished
    every second of the read. Will be looking forward to your next article 🙂

  12. Eelco says:

    Should the .join(”) part of the code be inside the “renderKeywords” function in the last example?

  13. Matt says:

    This is a great piece on using template strings. I came upon this after googling “js node append child vs template string html”.

    I’m trying to grow my understanding of modular JavaScript and struggle my way through React-Component-like modular code and I found myself writing createElement and appendChild a whole lot. With the ability to write it this way, is there any need to continue to use Node.appendChild etc.?

Leave a Reply

Your email address will not be published. Required fields are marked *