Swiftpack.co - Package - vapor-community/HTMLKit


Render lightning fast HTML templates in a typesafe way! By using Swift's powerful language features and a pre-rendering algorithm, will HTMLKit render insanely fast templates but also be able to catch bugs that otherwise might occur with other templating options.

Getting Started

Add the following in your Package.swift file

.package(url: "https://github.com/vapor-community/HTMLKit.git", from: "1.1.0"),

And register the provider and the different templates with in configure.swift

var renderer = HTMLRenderer()
try renderer.add(template: MyTemplate())

try services.register(HTMLKitProvider())

Some benchmarks ⚡

As mentioned HTMLKit is extremely fast since it pre-renders most of the template, and uses KeyPath's instead of decoding the context with Codable. But how much will faster will this make the rendering? By using the Leaf templating language as a benchmark, HTMLKit was 150x faster, and compared to Pointfree 16-25x faster.

The Leaf template used was a fairly complex template and HTMLKit rendered 128 documents in 0.00548 sec.

How do you use it? 🔧

The basics

Let's get started with the two main protocols to know.

  • ContextualTemplate: This is a protocol making it easy to render HTML views by giving you access to a lot of helper functions. But this needs a Context to render. This could be a struct, protocol etc.
  • StaticView: This is a protocol conforms to ContextualTemplate but needs no Context to render.

When creating a view, it is recommend to use either StaticView of ContextualTemplate, since the HTMLRenderer has functions that is tailored for these two protocols.

The building of a view is done in the build() function. Here you will need to return a CompiledTemplate which is fancy talk for something that can be represented as HTML. By default some base types like String, Int, Bool, Optional, UUID and Array<Compiledtemplate>. This means you in theory can return a pure String as the view, even to this is not recommended.

You also get access to some more interesting structs that represents the different HTML tags and attributes. The tags are computed variables and you can therefore access them by typing something like div. This will return an empty div tag like <div></div>. In order to add attributes, you can call div.class("simple-view"), this will result in <div class='simple-view'></div>. And lastly, in those tags that can contain a children, you can call div.class("simple-view").child("Hello World") and this will result in <div class='simple-view'>Hello World</div>.

Create a Template

When rendering a template you usually want some more advanced stuff and not only pure static HTML tags, so what do HTMLKit offer.

  • If statements: You can render an if where ever you like. Want to render a cookie message? HTMLKit got you covered. Want to add a class based on a context? HTMLKit got you covered here also.
  • For each loops: In some instances you might want to render a list of objects, and HTMLKit provides the tools for exactly this.
  • Representing variables: Again, HTMLKit got the tools for the job. Even raw or safely formatted HTML variables.

But what if you would like do some manipulation on the variable, how would this work? Since HTMLKit uses KeyPaths to represent variables, it is possible to reference a computed variable of any type. This means that you can capitalize a pure swift string by calling variable(\.string.capitalized). Isn't that amazing! This means you can add any extension to a swift type and use it in the template when you like.

So let's render a simple template that needs one String value and will display the string value in a <div> that has a class attribute and a <p> tag.

struct SimpleView: ContextualTemplate {

    struct Context {
        let value: String

    func build() -> CompiledTemplate {

Now how do we render the views?

In order to render the views effectively and cleanly, we need to add all the different views to a render struct. Bellow is an example of how to render a view with a context:

var renderer = HTMLRenderer()
try renderer.add(template: SimpleView())
try req.renderer().render(SimpleView.self, with: .init(value: "hello world"))

This would render:

<div class="simple-view">
    <p>Hello World</p>

You register and optimize the rendering by calling the add(view: StaticView) or add(template: ContextualTemplate), and render the view with the with render(TemplateBuilder.Type) or render(ContextualTemplate.Type, with: ContextualTemplate.Context).

Now since that was so simple. Let's step it up a bit by creating a base template and a view that renders some content, based on the input.

struct BaseView: StaticView {

    let title: String
    let body: CompiledTemplate

    func build() -> CompiledTemplate {

struct SomeView: ContextualTemplate {

    struct Context {
        let name: String
        let values: [SimpleView.Context]

        init(name: String, values: [String]) -> Context {
            return .init(
                name: name, 
                values: values.enumerated().map { .init(value: $0.element) }

    func build() -> CompiledTemplate {
                title: "Welcome"
                        "Hello ", variable(\.name), "!"
                        \.values.count > 0,
                        forEach(in:     \.values, 
                                render: SimpleView())
                            "There is no values!"
                        .if(\.name.isEmpty, add: .class("empty-nav")).child(
                            "This is a footer"


renderer.add(template: SomeView())
renderer.render(SomeView.self, with: .init(name: "Mats", values: ["First", "Second", "Third"])

This would render:

        <p>Hello Mats!</p>
        <div class='simple-view'>
        <div class='simple-view'>
        <div class='simple-view'>
        <footer class='allways'>
            This is a footer

More Feature Syntax

  • Variables:
    • A variable that is HTML safe = variable(\.title)
    • A variable that do not escape anything = variable(\.title, escaping: .unsafeNone)
    • A variable that is not in the current Context (example get a variable in superview) unsafeVariable(in: BaseTemplate.self, for: \.title) or unsafeVariable(... escaping: .unsafeNone)
  • Embed:
    • Where the sub view's Context is equal to the super view's Context = embed(SubView())
    • Where the sub view's Contextis variable of the super view's Context= embed(Subview(), withPath: \.subContext)
  • ForEach:
    • Where the super view's Context is an array of the sub view's Context = forEach(render: SubView())
    • Where the super view's Context variable is an array of the sub view's Context = forEach(in \.subContext, render: Subview()
  • If:
    • If the value is a Bool = runtimeIf(\.bool, div.child(...))
    • If the value is nil = runtimeIf(isNil: \.optional, div.child(...))
    • If the value is not nil = runtimeIf(isNotNil: \.optional, div.child(...))
    • If the value conforms to Equatable = runtimeIf(\.int == 2, div.child(...))
    • If the value conforms to Equatable = runtimeIf(\.int != 2, div.child(...))
    • If the value conforms to Comparable = runtimeIf(\.int < 2, div.child(...))
    • If the value conforms to Comparable = runtimeIf(\.int > 2, div.child(...))
    • It is also possible to use || and && for more complex statments. runtimeIf(\.bool || \.otherBool, div.child(...))
    • elseIfhas the same statments and is a method on the returned if. runtimeIf(...).elseIf(...)
    • and lastly else. runtimeIf(...).else(div.child(...))
  • Dynamic Attributes
    • In order to add attributes based on the Context you can use if's. div.if(\.bool, add: .checked)

Add custom node types by extending TemplateBuilder.

extension TemplateBuilder {
    var div: HTML.ContentNode { 
        return HTML.ContentNode(name: "div") 

Add non-dynamic attributes with an extension on AttributableNode. Or extend HTML.AttributeNodeif a dynamic is needed.

extension AttributableNode {
    func ariaLabelledby(_ values: CompiledTemplate...) -> Self {
        return add(.init(attribute: "aria-labelledby", value: values))

extension HTML.AttributeNode {
    static func dataToggle(_ toggle: CompiledTemplate) -> HTML.AttributeNode {
        return HTML.AttributeNode(attribute: "data-toggle", value: toggle)

Useful Resources to Get Started


Stars: 12
Help us keep the lights on


Used By

Total: 1


1.1.1 - Mar 11, 2019

Made the Request extension public, as this was left as internal by a mistake

1.1.0 - Mar 11, 2019


  • A Vapor HTMLKitProvider and an extension on Request for simpler usage.
  • All the nodes are dynamic by default, and therefore removed dynamic(...).
  • Added the possibility to reference context variables outside the specified context with unsafeVariable(...). Example to retrieve a value in a superview.
  • Changed forEachInContext(render: ...) to forEach(render: ...).
  • More flexible conditions when using && and ||.
  • Made TemplateBuilder internal as the developer should use StaticView or ContextualTemplate.
  • render(...) now returns a HTTPResponse and the old render function is therefore called renderRaw(...).