Saturday, March 7, 2026
HomeiOS DevelopmentHow you can create your first web site utilizing Vapor 4 and...

How you can create your first web site utilizing Vapor 4 and Leaf?


Let’s construct an online web page in Swift. Learn to use the model new template engine of the preferred server facet Swift framework.

Mission setup

Begin a model new venture through the use of the Vapor toolbox. In the event you don’t know what’s the toolbox or the way to set up it, you must learn my newbie’s information about Vapor 4 first.

// swift-tools-version:5.3
import PackageDescription

let bundle = Bundle(
    identify: "myProject",
    platforms: [
       .macOS(.v10_15)
    ],
    dependencies: [
        // 💧 A server-side Swift web framework.
        .package(url: "https://github.com/vapor/vapor", from: "4.32.0"),
        .package(url: "https://github.com/vapor/leaf", .exact("4.0.0-tau.1")),
        .package(url: "https://github.com/vapor/leaf-kit", .exact("1.0.0-tau.1.1")),
    ],
    targets: [
        .target(name: "App", dependencies: [
            .product(name: "Leaf", package: "leaf"),
            .product(name: "Vapor", package: "vapor"),
        ]),
        .goal(identify: "Run", dependencies: ["App"]),
        .testTarget(identify: "AppTests", dependencies: [
            .target(name: "App"),
            .product(name: "XCTVapor", package: "vapor"),
        ])
    ]
)

Open the venture by double clicking the Bundle.swift file. Xcode will obtain all of the required bundle dependencies first, then you definitely’ll be able to run your app (you might need to pick out the Run goal & the correct machine) and write some server facet Swift code.

Getting began with Leaf 4

Leaf is a strong templating language with Swift-inspired syntax. You should utilize it to generate dynamic HTML pages for a front-end web site or generate wealthy emails to ship from an API.

In the event you select a domain-specific language (DSL) for writing type-safe HTML (similar to Plot) you’ll should rebuild your whole backend utility if you wish to change your templates. Leaf is a dynamic template engine, this implies that you could change templates on the fly with out recompiling your Swift codebase. Let me present you the way to setup Leaf.

import Vapor
import Leaf

public func configure(_ app: Software) throws {

    app.middleware.use(FileMiddleware(publicDirectory: app.listing.publicDirectory))

    if !app.setting.isRelease {
        LeafRenderer.Choice.caching = .bypass
    }

    app.views.use(.leaf)

    strive routes(app)
}

With only a few traces of code you might be prepared to make use of Leaf. In the event you construct & run your app you’ll be capable to modify your templates and see the modifications immediately if reload your browser, that’s as a result of we’ve bypassed the cache mechanism utilizing the LeafRenderer.Choice.caching property. In the event you construct your backend utility in launch mode the Leaf cache shall be enabled, so it’s essential to restart your server after you edit a template.

Your templates ought to have a .leaf extension and they need to be positioned beneath the Sources/Views folder inside your working listing by default. You may change this habits by means of the LeafEngine.rootDirectory configuration and you may as well alter the default file extension with the assistance of the NIOLeafFiles supply object.

import Vapor
import Leaf
    
public func configure(_ app: Software) throws {

    app.middleware.use(FileMiddleware(publicDirectory: app.listing.publicDirectory))

    if !app.setting.isRelease {
        LeafRenderer.Choice.caching = .bypass
    }
    
    let detected = LeafEngine.rootDirectory ?? app.listing.viewsDirectory
    LeafEngine.rootDirectory = detected

    LeafEngine.sources = .singleSource(NIOLeafFiles(fileio: app.fileio,
                                                    limits: .default,
                                                    sandboxDirectory: detected,
                                                    viewDirectory: detected,
                                                    defaultExtension: "html"))
    
    app.views.use(.leaf)

    strive routes(app)

}

The LeafEngine makes use of sources to search for template areas if you name your render perform with a given template identify. You can even use a number of areas or construct your individual lookup supply if you happen to implement the LeafSource protocol if wanted.

import Vapor
import Leaf
    
public func configure(_ app: Software) throws {

    app.middleware.use(FileMiddleware(publicDirectory: app.listing.publicDirectory))

    if !app.setting.isRelease {
        LeafRenderer.Choice.caching = .bypass
    }
    
    let detected = LeafEngine.rootDirectory ?? app.listing.viewsDirectory
    LeafEngine.rootDirectory = detected

    let defaultSource = NIOLeafFiles(fileio: app.fileio,
                                     limits: .default,
                                     sandboxDirectory: detected,
                                     viewDirectory: detected,
                                     defaultExtension: "leaf")

    let customSource = CustomSource()

    let multipleSources = LeafSources()
    strive multipleSources.register(utilizing: defaultSource)
    strive multipleSources.register(supply: "custom-source-key", utilizing: customSource)

    LeafEngine.sources = multipleSources
    
    app.views.use(.leaf)

    strive routes(app)
}

struct CustomSource: LeafSource {

    func file(template: String, escape: Bool, on eventLoop: EventLoop) -> EventLoopFuture {
        /// Your {custom} lookup methodology comes right here...
        return eventLoop.future(error: LeafError(.noTemplateExists(template)))
    }
}

Anyway, this can be a extra superior matter, we’re good to go along with a single supply, additionally I extremely advocate utilizing a .html extension as a substitute of leaf, so Xcode may give us partial syntax spotlight for our Leaf recordsdata. Now we’re going to make our very first Leaf template file. 🍃

NOTE: You may allow fundamental syntax highlighting for .leaf recordsdata in Xcode by selecting the Editor ▸ Syntax Coloring ▸ HTML menu merchandise. Sadly if you happen to shut Xcode you must do that repeatedly for each single Leaf file.

Create a brand new file beneath the Sources/Views listing known as index.html.



  
    
    
    #(title)
  
  
    
  

Leaf provides you the flexibility to place particular constructing blocks into your HTML code. These blocks (or tags) are at all times beginning with the # image. You may consider these as preprocessor macros (if you’re accustomed to these). The Leaf renderer will course of the template file and print the #() placeholders with precise values. On this case each the physique and the title key’s a placeholder for a context variable. We’re going to set these up utilizing Swift. 😉

After the template file has been processed it’ll be rendered as a HTML output string. Let me present you the way this works in apply. First we have to reply some HTTP request, we are able to use a router to register a handler perform, then we inform our template engine to render a template file, we ship this rendered HTML string with the suitable Content material-Sort HTTP header worth as a response, all of this occurs beneath the hood mechanically, we simply want to write down a couple of traces of Swift code.

import Vapor
import Leaf

func routes(_ app: Software) throws {

    app.get { req in
        req.leaf.render(template: "index", context: [
            "title": "Hi",
            "body": "Hello world!"
        ])
    }
}

The snippet above goes to your routes.swift file. Routing is all about responding to HTTP requests. On this instance utilizing the .get you’ll be able to reply to the / path. In different phrases if you happen to run the app and enter http://localhost:8080 into your browser, you must be capable to see the rendered view as a response.

The primary parameter of the render methodology is the identify of the template file (with out the file extension). As a second parameter you’ll be able to go something that may characterize a context variable. That is normally in a key-value format, and you should use nearly each native Swift sort together with arrays and dictionaries. 🤓

Once you run the app utilizing Xcode, don’t neglect to set a {custom} working listing, in any other case Leaf received’t discover your templates. You can even run the server utilizing the command line: swift run Run.

How you can create your first web site utilizing Vapor 4 and Leaf?

Congratulations! You simply made your very first webpage. 🎉

Inlining, analysis and block definitions

Leaf is a light-weight, however very highly effective template engine. In the event you study the essential rules, you’ll be capable to utterly separate the view layer from the enterprise logic. If you’re accustomed to HTML, you’ll discover that Leaf is simple to study & use. I’ll present you some useful ideas actual fast.

Splitting up templates goes to be important if you’re planning to construct a multi-page web site. You may create reusable leaf templates as elements that you could inline afterward.

We’re going to replace our index template and provides a chance for different templates to set a {custom} title & description variable and outline a bodyBlock that we are able to consider (or name) contained in the index template. Don’t fear, you’ll perceive this complete factor if you have a look at the ultimate code.



  
    
    
    #(title)
    
  
  
    
#bodyBlock()

The instance above is a extremely good place to begin. We may render the index template and go the title & description properties utilizing Swift, after all the bodyBlock could be nonetheless lacking, however let me present you the way can we outline that utilizing a special Leaf file known as residence.html.

#let(description = "That is the outline of our residence web page.")
#outline(bodyBlock):

#(header)

#(message)

#enddefine #inline("index")

Our residence template begins with a relentless declaration utilizing the #let syntax (you may as well use #var to outline variables), then within the subsequent line we construct a brand new reusable block with a multi-line content material. Contained in the physique we are able to additionally print out variables mixed with HTML code, each single context variable can be obtainable inside definition blocks. Within the final line we inform the system that it ought to inline the contents of our index template. Which means that we’re actually copy & paste the contents of that file right here. Consider it like this:

#let(description = "That is the outline of our residence web page.")
#outline(bodyBlock):

#(header)

#(message)

#enddefine #(title)
#bodyBlock()

As you’ll be able to see we nonetheless want values for the title, header and message variables. We don’t should take care of the bodyBlock anymore, the renderer will consider that block and easily substitute the contents of the block with the outlined physique, that is how one can think about the template earlier than the variable alternative:

#let(description = "That is the outline of our residence web page.")


  
    
    
    #(title)
    
  
  
    

#(header)

#(message)

Now that’s not probably the most correct illustration of how the LeafRenderer works, however I hope that it’ll show you how to to know this entire outline / consider syntax factor.

NOTE: You can even use the #consider tag as a substitute of calling the block (bodyBlock() vs #consider(bodyBlock), these two snippets are primarily the identical).

It’s time to render the web page template. Once more, we don’t should take care of the bodyBlock, because it’s already outlined within the residence template, the outline worth additionally exists, as a result of we created a brand new fixed utilizing the #let tag. We solely should go across the title, header and message keys with correct values as context variables for the renderer.

app.get { req in
    req.leaf.render(template: "residence", context: [
        "title": "My Page",
        "header": "This is my own page.",
        "message": "Welcome to my page!"
    ])
}

It’s attainable to inline a number of Leaf recordsdata, so for instance you’ll be able to create a hierarchy of templates similar to: index ▸ web page ▸ welcome, simply comply with the identical sample that I launched above. Price to say that you could inline recordsdata as uncooked recordsdata (#inline("my-file", as: uncooked)), however this manner they received’t be processed throughout rendering. 😊

LeafData, loops and circumstances

Spending some {custom} knowledge to the view is just not that arduous, you simply have to evolve to the LeafDataRepresentable protocol. Let’s construct a brand new checklist.html template first, so I can present you a couple of different sensible issues as effectively.

#let(title = "My {custom} checklist")
#let(description = "That is the outline of our checklist web page.")
#var(heading = nil)
#outline(bodyBlock):

    #for(todo in todos):
  • #if(todo.isCompleted):✅#else:❌#endif #(todo.identify)
  • #endfor
#enddefine #inline("index")

We declare two constants so we don’t should go across the title and outline utilizing the identical keys as context variables. Subsequent we use the variable syntax to override our heading and set it to a 0 worth, we’re doing this so I can present you that we are able to use the coalescing (??) operator to chain optionally available values. Subsequent we use the #for block to iterate by means of our checklist. The todos variable shall be a context variable that we setup utilizing Swift afterward. We are able to additionally use circumstances to verify values or expressions, the syntax is just about simple.

Now we simply should create a knowledge construction to characterize our Todo objects.

import Vapor
import Leaf

struct Todo {
    let identify: String
    let isCompleted: Bool
}

extension Todo: LeafDataRepresentable {

    var leafData: LeafData {
        .dictionary([
            "name": name,
            "isCompleted": isCompleted,
        ])
    }
}

I made a brand new Todo struct and prolonged it so it may be used as a LeafData worth throughout the template rendering course of. You may lengthen Fluent fashions identical to this, normally you’ll have to return a LeafData.dictionary sort together with your object properties as particular values beneath given keys. You may lengthen the dictionary with computed properties, however this can be a nice method to conceal delicate knowledge from the views. Simply utterly ignore the password fields. 😅

Time to render an inventory of todos, that is one attainable strategy:

func routes(_ app: Software) throws {

    app.get { req -> EventLoopFuture in
        let todos = [
            Todo(name: "Update Leaf 4 articles", isCompleted: true),
            Todo(name: "Write a brand new article", isCompleted: false),
            Todo(name: "Fix a bug", isCompleted: true),
            Todo(name: "Have fun", isCompleted: true),
            Todo(name: "Sleep more", isCompleted: false),
        ]
        return req.leaf.render(template: "checklist", context: [
            "heading": "Lorem ipsum",
            "todos": .array(todos),
        ])
    }
}

The one distinction is that we’ve got to be extra specific about sorts. Which means that we’ve got to inform the Swift compiler that the request handler perform returns a generic EventLoopFuture object with an related View sort. The Leaf renderer works asynchronously in order that’s why we’ve got to work with a future worth right here. In the event you don’t how how they work, please examine them, futures and guarantees are fairly important constructing blocks in Vapor.

The very very last thing I need to discuss is the context argument. We return a [String: LeafData] sort, that’s why we’ve got to place an extra .array initializer across the todos variable so the renderer will know the precise sort right here. Now if you happen to run the app you must be capable to see our todos.

Abstract

I hope that this tutorial will show you how to to construct higher templates utilizing Leaf. In the event you perceive the essential constructing blocks, similar to inlines, definitions and evaluations, it’s going to be very easy to compose your template hierarchies. If you wish to study extra about Leaf or Vapor you must verify for extra tutorials within the articles part or you should purchase my Sensible Server Facet Swift ebook.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments