Templating with Mustache: an Interactive Tutorial

Published: December 20, 2024
Written by:
Natan Rolnik
Natan Rolnik

When writing your own tools, generating code is a very frequent need. For example, in the context of iOS development, SwiftGen is a tool that creates code from .xcassets files, storyboards, and Localizable.strings, allowing developers to rely on type safe code instead of string-based APIs.

Usually these needs are grouped into patterns, technically called templates: they contain static text and dynamic content (variables, objects and lists) that can be accessed from within the template. Specific programs are able to process these templates: templating engines. They take an input, composed of a template and the dynamic content (often called context), and generate an output.

In the Swift ecosystem, there are a few popular templating engines, with slightly different specifications: Stencil, Leaf (from Vapor), and SwiftMustache. The last one is a Swift implementation for the Mustache standard, written by Adam Fowler and is part of the Hummingbird project:

There wasn’t a full implementation of Mustache written in Swift that compiled on Linux. Mustache is a web standard and we should have an implementation of it in our Swift ecosystem.

Adam Fowler

In this interactive post, you’ll learn how to harness the power of Mustache in your Swift programs, with hands-on live examples that let you experiment and learn by doing. Let’s get started! 🚀

Adding SwiftMustache via SPM

To use Mustache from your CLI tool or your server, start by adding first the package dependency to your Package.swift file:

...
dependencies: [
  .package(
    url: "https://github.com/hummingbird-project/swift-mustache.git",
    from: "2.0.0"
  ),
]
...

After that, add the target dependency to your target:

...
targets: [
  .executableTarget(
    name: "my-great-tool",
    dependencies: [
      .product(name: "Mustache", package: "swift-mustache"),
    ]
  )
]
...

The Basics

Now you’re ready to import Mustache and start using it. The simplest way to use it, is initializing a MustacheTemplate from a string:

let template = try MustacheTemplate(string: "Hello, {{ firstName }}!")

You can notice the double curly brackets wrapping a variable, called firstName. When rendering this template, Mustache will look for this key in the object you pass in as the input. For the sake of this example, let’s create a simple JSON dictionary, and see how Mustache renders it.

Template
Context

Click the Render button to see the result. Then try adding a lastName key to the JSON dictionary, and also update the template to check how it changes the output.

Using Structs (Or Other Types) as Input

In the previous example, the context was a dictionary. But due to how SwiftMustache was implemented with reflection and Mirror, you can also use other types, like structs.

struct Person {
  let firstName: String
  let lastName: String
}

let person = Person(firstName: "John", lastName: "Appleseed")
let template = try MustacheTemplate(
  string: "Hello, {{ firstName }} {{ lastName }}!"
)
let output = template.render(person)
// Renders: "Hello, John Appleseed!"

This is very useful when you want to generate code for your own types, guaranteeing that the input data is always consistent with your own types.

Sections

Mustache defines itself as a logic-less templating engine, but it actually supports conditionals and iterating through arrays. This is because, differently from other templating engines, Mustache doesn’t use if/else statements, nor loops. Instead, it uses sections. According to the definition in the Mustache manual:

Sections render blocks of text zero or more times, depending on the value of the key in the current context.

For example, you can use a section to check if a key is present in the context, or if it’s true. Alternatively, it also can be used to iterate through an array.

Conditional Values

To open a section, you use the {{#key}} syntax, and {{/key}} to close it:

Template
Context

Click to render the template. Then, try changing the isAllowed key to false, or just delete it, to see how the output changes. If you look closely, you’ll notice this happens because of the {{^isAllowed}} inverted section: it becomes active when the key is not present or is false.

Similarly, you can check the presence of a key, and render within a section only if it’s present:

Template
Context

In the example above, the superPower key is not present in the context, so the section is not rendered. Try adding it to the context, something like "Crazy dribbling", and see the result.

Notice how, inside the optional superPower section, the value is accessed with {{ . }} syntax. This is useful when accessing the current value within a section, as you’ll see in the next paragraph about arrays.

Iterating Through Arrays

With Mustache, you can iterate through arrays without having to write a loop. The great thing about it, is that you use the same section syntax, but instead of the value for the key being a boolean or a string, it’s an array.

Template
Context

In the example above, fruits is an array, and the section is rendered once for each element in the array. Because each element of the array is a string, and not another object, the {{ . }} syntax is used to access the current element.

Alternatively, you can also access nested keys of the current element, by passing the key inside the curly brackets:

Template
Context

Try adding more products to the array, and see how the output changes, or add new keys to the products, like description, and update the template accordingly to reflect the new structure.

Functions

Another great capability of Mustache is the ability to use functions in the template - they run at the time of rendering. For example, you can render the time and date, or pick a random element from an array. While in other implementations of Mustache you can use a regular function or closure, in SwiftMustache you do so by using a MustacheLambda, and initializing it with a block:

Template (editing disabled)
Context (editing disabled)

You can click the Render button multiple times, to see how the output changes, indicating that the functions are executed at the time of rendering.

But there’s also another interesting way of using functions. Mustache allows to use a function itself as a section. For example, imagine you want to wrap a string in a <strong> tag:

Template (editing disabled)
Context (editing disabled)

Template Libraries

Initializing a MustacheTemplate from a string is great for simple cases, but as you write longer and more complex templates, you’ll want to have them separated into different files. This is where template libraries come in: they group different templates together, identifying each by name.

There are two ways to create a library. The first is manually, where you register each template by calling the register(_:named:) method. The other option, more convenient, is add a folder of templates as a resource in your target and use it to initialize a library:

#1
func loadLibrary() async throws -> MustacheLibrary {
  #2
  guard let path = Bundle.module.resourcePath else {
      throw Error.resourcesBundleNotFound
  }

  #3
  let templatesPath = path.appending("/Templates")

  #4
  return try await MustacheLibrary(directory: templatesPath)
}

The code above can be used from an executable SPM target - a CLI tool or a server - and it does the following:

  1. Declare a function that returns a MustacheLibrary - it can throw an error, and also uses async to make it non-blocking
  2. Look for the resources path provided by the Swift Package Manager, and throw an error if it’s not found
  3. Assuming you added all your templates to a Templates folder, append this path to the resources path
  4. Finally, initialize the MustacheLibrary with the templates directory

From there, you can use the MustacheLibrary to either get a template by its file name, or to directly render it:

let library = try await loadLibrary()
let context = ["email": "john@example.com"]

// Get a template by its file name
// It's an optional because the file might not exist.
if let template = try library.template(named: "welcome-email") {
  let output = try template.render(context)
}

// Or render directly from the library
let output = try library.render(template: "welcome-email", context: context)

Tags

Mustache supports different tags, which are used to add more functionality to the templates. This section will cover three of the most important ones.

Escaping HTML

As one of the most common use cases for templating is generating HTML, Mustache automatically escapes variables in a way that’s compatible with HTML.

For example, if you wanted to render the title of a website, and you use characters such as &, <, > or ", they will be properly escaped: Tim & Craig becomes Tim & Craig.

In case you want to render raw HTML, you can use three curly brackets, instead of two: {{{ rawHTML }}}. You can play with the example below to see how it works. Add another curly brackets for the description key, making it {{{ description }}}, and see how they will differ once rendered.

Template
Context

Comments

Just as regular code, you can also add comments to templates, by using the {{! comment }} syntax:

{{! This is a comment, will not show up in the output }}
Welcome to {{ websiteName }}!

Partials

Sometimes, when writing long templates, you’ll want to break them apart into smaller pieces. This has two main advantages. First, it allows you to split the templates into smaller bits, making it easier to read and understand. Second, it allows you to reuse the same template in multiple places: this is where partials come in.

{{> header }}

{{#items}}
  {{> item }}
{{/items}}

In the template above, there are two partials: header and item. When Mustache renders the template, it will first look for the a template named header in the library. After that, if items is not empty, it will look for another template, this time item, and render it using each item as the context.

Explore Further

Using templates is a very powerful capability you can implement in your own tools, and Mustache is an elegant way to do it. Be it for code generation, writing HTML, or any other need, it’s a great skill to have!

Special thanks to Adam Fowler for his work on SwiftMustache, and for reviewing this post.

Here are some additional resources you can use to learn more about Mustache:

If you have any questions, comments, or feedback on this tutorial, drop us a line at X or Mastodon.

See you at the next post. Have a good one!
Swift, Xcode, the Swift Package Manager, iOS, macOS, watchOS and Mac are trademarks of Apple Inc., registered in the U.S. and other countries.

© Swift Toolkit, 2024-2025