Leaf is Vapor's component for rendering dynamic templates. Rather than writing HTML strings by hand in our router, we can write leaf templates that allow us to mix HTML with code. Since Leaf is a separate package, we will show how to integrate this into your project from scratch, to get an overview of how dependencies are assembled in a Vapor project.
Episode Links Vapor Leaf Leaf Docs Starting from Scratch We'll start a new project and start building it: $ vapor new leaf-example $ cd leaf-example $ vapor build Once this is done, we'll generate an Xcode project for it... $ vapor xcode Removing Unused Code This project comes with some stuff we don't need (yet). Let's delete it so we can focus just on Leaf templates. In Package.swift, remove the package line for fluent-sqlite as well as the dependency FluentSQLite in the targets section. We'll also delete the Controllers and Models folder. In the configure.swift, delete all of the lines that mention databases, migrations, or Fluent. We'll cover this later. Now build and ensure there are no errors. Adding Leaf In Package.swift, we'll add the dependency: .package(url: "https://github.com/vapor/leaf.git", from: "3.0.1") The version number here matters, and you should choose the latest version that is compatible with your version of Vapor. We'll also need to ensure that our App target is properly linked to this new dependency. To do that, update the dependencies line to include "Leaf" like this: .target(name: "App", dependencies: ["Vapor", "Leaf"]) Now that we've declared that we depend on Leaf, we need to tell vapor to download and build it again: $ vapor build Since we've changed our project's settings, we'll also need to regenerate the Xcode project: $ vapor xcode Configuring Leaf Open up configure.swift. We need to import Leaf at the top of the file, then add these lines to the configure function: try services.register(LeafProvider()) config.prefer(LeafRenderer.self, for: ViewRenderer.self) Using Leaf Let's go to our routes file and add a route that renders a view. router.get("hello") { req in return try req.view().render("hello") } This will look for Resources/Views/hello.leaf, so we'll need to create those directories and then create an empty file called hello.leaf. <h1>Hello from Leaf!</h1> If we build and run this we can visit http://localhost:8080/hello in our browser to see our new template being rendered! Passing Dynamic Data to Leaf Let's create a new route that accepts a username, like hello/ben. We'll pass this data to the view with some structured data. The context we'll pass to the view can be anything that conforms to Codable. struct UserPage { var user: String } Then we can define our route that passes this data to the view... router.get("hello", String.parameter) { req in let user = try req.parameters.next(String.self) let context = UserPage(user: user) return try req.view().render("hello", context) } This may confuse the compiler a bit, and we'll need to help it out by describing our return type explicitly like this: router.get("hello", String.parameter) { req -> Future<View> in ... What is a future? Don't worry about this for now, we will dig into this in detail in a future episode. Now, back in hello.leaf, add the following: <h2>You are logged in as #(user)</h2> Note that the context object we passed is directly accessible to the template. The local members of the object become variables that we can access in the template. We'll look at this in more depth later in this series. Make sure to re-build and run the server to pick up the route changes we made. Then navigate to http://localhost:8080/hello/ben and see your dynamic data!