Emit HTML with a DSL

Estimated reading time: 2 minutes

This feature integrates with kotlinx.html to directly emit HTML using Chunked transfer encoding without having to keep memory for the whole HTML.

This feature is defined in the class io.ktor.html.HtmlContent in the artifact io.ktor:ktor-html-builder:$ktor_version.
dependencies { compile "io.ktor:ktor-html-builder:$ktor_version" }
<project> ... <dependencies> <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-html-builder</artifactId> <version>${ktor.version}</version> </dependency> </dependencies> </project>

Installing

This feature doesn’t require installation.

Basic Usage

When generating the response, instead of calling the respond/respondText methods, you have to call ApplicationCall.respondHtml:

call.respondHtml {
    head {
        title { +"Async World" }
    }
    body {
        h1(id = "title") {
            +"Title"
        }
    }
}

For documentation about generating HTML using kotlinx.html, please check its wiki.

Templates & Layouts

In addition to plain HTML generation with the DSL, ktor exposes a simple typed templating engine. You can use it to generate complex layouts in a typed way. It is pretty simple, yet powerful:

call.respondHtmlTemplate(MulticolumnTemplate()) {
    column1 {
        +"Hello, $name"
    }
    column2 {
        +"col2"
    }
}

class MulticolumnTemplate(val main: MainTemplate = MainTemplate()) : Template<HTML> {
    val column1 = Placeholder<FlowContent>()
    val column2 = Placeholder<FlowContent>()
    override fun HTML.apply() {
        insert(main) {
            menu {
                item { +"One" }
                item { +"Two" }
            }
            content {
                div("column") {
                    insert(column1)
                }
                div("column") {
                    insert(column2)
                }
            }
        }
    }
}

class MainTemplate : Template<HTML> {
    val content = Placeholder<HtmlBlockTag>()
    val menu = TemplatePlaceholder<MenuTemplate>()
    override fun HTML.apply() {
        head {
            title { +"Template" }
        }
        body {
            h1 {
                insert(content)
            }
            insert(MenuTemplate(), menu)
        }
    }
}

class MenuTemplate : Template<FlowContent> {
    val item = PlaceholderList<UL, FlowContent>()
    override fun FlowContent.apply() {
        if (!item.isEmpty()) {
            ul {
                each(item) {
                    li {
                        if (it.first) b {
                            insert(it)
                        } else {
                            insert(it)
                        }
                    }
                }
            }
        }
    }
}

You have to define classes implementing Template<TFlowContent>, overriding the TFlowContent.apply method and optionally define Placeholder or TemplatePlaceholder properties just like in the example.

When generating the template with call.respondHtmlTemplate(MulticolumnTemplate()) { }, you will get the template as receiver, and will be able to access the placeholders defined as properties in a typed way.