Building stylesheets using Leaf

Let me show you how to use the LeafFileMiddleware component to render public HTML files and CSS stylesheets in Vapor 4.


A quick CSS demo project

The very first step is to add Leaf as a dependency to your project. You should note that Leaf 4 is not finished yet and these brand new features are only available from the tau pre-release.

// swift-tools-version:5.3
import PackageDescription

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

We are ready to import Leaf in your Swift files, since there is a new LeafFileMiddleware available as part of Leaf we're going to create some publicly available template files and use this middleware to render them. Create a new Public directory inside the root folder of the project and place an new index.html file there. You can also use a .leaf extension, but for the sake of simplicity (and Xcode syntax highlighting reasons) we're going to use the .html extension this time.

<!DOCTYPE html>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="/css/style.css">

Pretty basic HTML5 boilerplate code, except that we'll print the title using a Leaf tag. We're going to set a value for this context variable through some Swift code in a moment. In the head section we also import our css/style.css stylesheet file. Now you should create a css folder inside the Public directory and place a style.css file inside of it.

* {
    margin: 0;
    padding: 0;
body {
    font-family: -apple-system, system-ui, BlinkMacSystemFont, "Helvetica", "Segoe UI", Roboto, Ubuntu;
    font-size: 16px;
    line-height: 1.4em;
    background: #(background);
h1 {
    padding: #(padding);
@media (max-width: 599px) {}
@media (min-width: 600px) {}
@media (min-width: 900px) {}
@media (min-width: 1200px) {}
@media (min-width: 1800px) {}

Since this file is "secretly" a leaf template file we can use the #(variable) syntax to print out values. We are going to pass a background color key and a padding key with some custom values as context variables.

Now let me show you how to configure this new LeafFileMiddleware, so we can render both our html and css templates.

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

    if !app.environment.isRelease {
        LeafRenderer.Option.caching = .bypass

    LeafFileMiddleware.defaultMediaType = .html
    LeafFileMiddleware.processableExtensions = ["leaf", "html", "css", "js"]
    LeafFileMiddleware.contexts = [
        .css: [
            "background": "#eee",
            "padding": "16px",
        .html: [
            "title": "Hello world!"
    if let lfm = LeafFileMiddleware(publicDirectory: {

First we disable the cache, but that's a pretty obvious chunk of code, next we set the default media type to html. This will be used to set the Content-Type header if the file extension in the request is an unknown type. The processableExtensions property will tell the LeafFileMiddleware to process and render only these files, everything else with a different extension will be streamed just like when you use a regular FileMiddleware.

As you can see we can set different context values for specific media types, in our case all the css files can use the background and padding properties and every html file can take advantage of the title context variable. It is also possible to set them through a subscript syntax:

LeafFileMiddleware[.css] = [
    "background": "green",
    "padding": "16px",

LeafFileMiddleware[.html] = [
    "title": "Hello world!"

The very last step is to create the actual middleware with a publicDirectory argument. This directory is the location where the system will look for publicly available files and if needed they can be processed as regular Leaf templates. You can also setup directory indexing through the LeafFileMiddleware, but that's a different topic.

If you navigate to the http://localhost:8080/index.html address you should see your rendered index.html file with the right stylesheet applied to it. Of course you can register a custom route and render your templates using the usual Resources / Views location if needed, but I just wanted to show you this cool trick, since it enables us to serve public files using a more dynamic approach.

Share this article on Twitter.
Thank you. 🙏

Picture of Tibor Bödecs

Tibor Bödecs

Creator of (weekly Swift articles), server side Swift enthusiast, full-time dad. -- Follow me & feel free to say hi. 🤘🏻 -- #iOSDev #SwiftLang

Twitter · GitHub


100% Swift news, delivered right into your mailbox

Updates about the latest Swift news including my articles and everything what happened in the Swift community as well.

Subscribe now