Skip to content

Leaf Overview

Leaf is a powerful templating language with Swift-inspired syntax. You can use it to generate dynamic HTML pages for a front-end website or generate rich emails to send from an API.

This guide will give you an overview of Leaf's syntax and the available tags.

Template syntax

Here is an example of a basic Leaf tag usage.

There are #count(users) users.

Leaf tags are made up of four elements:

  • Token #: This signals the leaf parser to begin looking for a tag.
  • Name count: that identifies the tag.
  • Parameter List (users): May accept zero or more arguments.
  • Body: An optional body can be supplied to some tags. This is similar to Swift's trailing-closure syntax.

There can be many different usages of these four elements depending on the tag's implementation. Let's look at a few examples of how Leaf's built-in tags might be used:

#(variable)
#embed("template")
#set("title") { Welcome to Vapor }
#count(friends)
#for(friend in friends) { <li>#(friend.name)</li> }

Leaf also supports many expressions you are familiar with in Swift.

  • +
  • >
  • ==
  • ||
  • etc.
#if(1 + 1 == 2) {
    Hello!
}

Context

In the example from Getting Started, we used a [String: String] dictionary to pass data to Leaf. However, you can pass anything that conforms to Encodable. It's actually preferred to use Encodable structs since [String: Any] is not supported. This means you can not pass in an array, and should instead wrap it in a struct:

struct WelcomeContext: Encodable {
    var title: String
    var numbers: [Int]
}
return try req.view().make("home", WelcomeContext(title: "Hello!", numbers: [42, 9001]))

That will expose title and numbers to our Leaf template, which can then be used inside tags. For example:

<h1>#(title)</h1>
#for(number in numbers){
    <p>#(number)</p>
}

Usage

Here are some common Leaf usage examples.

Conditions

Leaf is able to evaluate a range of conditions using its #if tag. For example, if you provide a variable it will check that variable exists in its context:

#if(title) {
    The title is #(title)
} else {
    No title was provided.
}

You can also write comparisons, for example:

#if(title == "Welcome") {
    This is a friendly web page.
} else {
    No strangers allowed!
}

If you want to use another tag as part of your condition, you should omit the # for the inner tag. For example:

#if(lowercase(title) == "welcome") {
    This is a friendly web page.
} else {
    No strangers allowed!
}

Just like in Swift, you can also use else if statements:

#if(title == "Welcome") {
    This is a friendly web page.
} else if (1 == 2) {
    What?
} else {
    No strangers allowed!
}

Loops

If you provide an array of items, Leaf can loop over them and let you manipulate each item individually using its #for tag.

For example, we could update our Swift code to provide a list of planets:

struct SolarSystem: Codable {
    let planets = ["Venus", "Earth", "Mars"]
}

return try req.view().render(..., SolarSystem())

We could then loop over them in Leaf like this:

Planets:
<ul>
#for(planet in planets) {
    <li>#(planet)</li>
}
</ul>

This would render a view that looks like:

Planets:
- Venus
- Earth
- Mars

Leaf provides some extra variables inside a #for loop to give you more information about the loop's progress:

  • The isFirst variable is true when the current iteration is the first one.
  • The isLast variable is true when it's the last iteration.
  • The index variable will be set to the number of the current iteration, counting from 0.

Here's how we could use a loop variable to print just the first name in our array:

#for(planet in planets) {
    #if(isFirst) { #(planet) is first! }
}

Embedding templates

Leaf’s #embed tag allows you to copy the contents of one template into another. When use this, you should always omit the template file's .leaf extension.

Embedding is useful for copying in a standard piece of content, for example a page footer or advert code:

#embed("footer")

This tag is also useful for building one template on top of another. For example, you might have a master.leaf file that includes all the code required to lay out your website – HTML structure, CSS and JavaScript – with some gaps in place that represent where page content varies.

Using this approach, you would construct a child template that fills in its unique content, then embeds the parent template that places the content appropriately. To do this, you can use the #set and #get tags to store and later retrieve content from the context.

For example, you might create a child.leaf template like this:

#set("body") {
    <p>Welcome to Vapor!</p>
}

#embed("master")

This stores some HTML in the context as body using #set. We then embed master.leaf which will render body along with any other context variables passed in from Swift. For example, master.leaf might look like this:

<html>
    <head>
        <title>#(title)</title>
    </head>
    <body>#get(body)</body>
</html>

Here we are using #get to fetch the content previously stored in the context. When passed ["title": "Hi there!"] from Swift, child.leaf will render as follows:

<html>
    <head>
        <title>Hi there!</title>
    </head>
    <body><p>Welcome to Vapor!</p></body>
</html>

Comments

You can write single or multiline comments with Leaf. They will be discarded when rendering the view.

#// Say hello to the user
Hello, #(name)!

Multi-line comments are opened with #/* and closed with */.

#/*
     Say hello to the user
*/
Hello, #(name)!

Other tags

#date

The #date tag formats dates into a readable string.

render(..., ["now": Date()])
The time is #date(now)

You can pass a custom date formatter string as the second argument. See Swift's DateFormatter for more information.

The date is #date(now, "yyyy-MM-dd")

#capitalize

The #capitalize tag uppercases the first letter of any string.

#capitalize(name)

#contains

The #contains tag accepts an array and a value as its two parameters, and returns true if the array in parameter one contains the value in parameter two.

#if(contains(planets, "Earth")) {
    Earth is here!
} else {
    Earth is not in this array.
}

#count

The #count tag returns the number of items in an array. For example:

Your search matched #count(matches) pages.

#lowercase

The #lowercase tag lowercases all letters in a string.

#lowercase(name)

#uppercase

The #uppercase tag uppercases all letters in a string.

#uppercase(name)