repositories {
jcenter()
}
dependencies {
// JVM version of the HTML library
implementation("dev.scottpierce.kotlin-html:kotlin-html-writer-jvm:0.7.24")
// If you are using Ktor, you can use this as well
implementation("dev.scottpierce.kotlin-html:kotlin-html-ktor:0.7.24")
}
repositories {
jcenter()
}
kotlin {
commonMain {
dependencies {
implementation("dev.scottpierce.kotlin-html:kotlin-html-writer:0.7.24")
}
}
}
If you want to see a website that has been built with this framework, take a look at https://www.arseldrivingschool.com
fun main() {
// Choose a HtmlOutput implementation. StringHtmlOutput is good for testing.
val output: HtmlOutput = StringHtmlOutput(options = WriteOptions.readable)
// Writes the main page to the HtmlOutput
output.mainPage()
// Prints out the written page
println(output)
}
fun HtmlOutput.mainPage() {
html(DocType.Html) {
head {
styleSheet {
style("body") {
margin(0.px)
padding(0.px)
color(r = 100, g = 100, b = 100)
}
style(".class2") {
backgroundColor(Color("#555555"))
}
}
}
body {
navigation()
aboutSection()
footer()
}
}
}
fun BodyContext.navigation() {
div(
id = "navigation",
style = {
// Inline style here
margin(1.rem)
}
) {
// Navigation HTML here
}
}
fun BodyContext.aboutSection() {
section(id = "about", classes = "class1 class2") {
// About section HTML here
}
}
fun BodyContext.footer() {
span(style = { /* Inline span style here */ }) {
+"Footer Text"
}
}
While the kotlin html writer allows for working with CSS directly, the style builder artifacts offer a more convenient way of dealing with style sheets and media queries. Style builder allows you to write to a single style sheet from anywhere in your HTML document. This means you can keep your styles closer to your elements, and also makes it easier to only write the styles if you are using them. It also offers a much more convenient syntax for interacting with media queries by allowing you to embed media query styles within already defined styles, keeping everything in one place.
- Import the style builder artifacts:
dependencies {
implementation("dev.scottpierce.kotlin-html:kotlin-html-style-builder:0.7.24")
}
Style Builders allow you name media queries. It's easy to make your own, but we provide some prebuild ones for the names
phone
, tablet
, and desktop
. Import what you want to use.
dependencies {
implementation("dev.scottpierce.kotlin-html:kotlin-html-style-builder-phone:0.7.24")
implementation("dev.scottpierce.kotlin-html:kotlin-html-style-builder-tablet:0.7.24")
implementation("dev.scottpierce.kotlin-html:kotlin-html-style-builder-desktop:0.7.24")
}
- Specify where in your
head
that you want your style builder inserted
html {
head {
insertStyleBuilder {
media(StyleBuilder.TABLET, "(min-width: 481px) and (max-width: 767px)")
media(StyleBuilder.PHONE, "(min-width: 320px) and (max-width: 480px)")
}
}
// ...
}
- Build your style from anywhere in your HTML document:
html {
// ...
body {
style(".example1") {
backgroundColor(255, 255, 255)
phone {
backgroundColor(0, 0, 0)
}
tablet {
backgroundColor(55, 55, 55)
}
}
div(classes = "example1") {
// ...
}
div(
id = "example2",
styleBuilder = {
backgroundColor(255, 255, 255)
phone {
backgroundColor(0, 0, 0)
}
tablet {
backgroundColor(55, 55, 55)
}
}
) {
// ...
}
}
}
You should only use this library if you are comfortable with the following:
- Potential API Changes - Until 1.0 the API is potentially unstable, although there have been a few major API revisions so far, and we're feeling more confident.
- Missing HTML Elements / Style Properties - Until this library hits 1.0 there is a good chance there are missing html elements that you
need. You may need to contribute a PR or two. I'll do my best to be responsive and won't let a PR sit for weeks.
- If you add an element, please make sure you add it via the generator module
- Add an Element
- Add a Style Property
- If you add an element, please make sure you add it via the generator module
- You don't need a read / introspection API for the DOM.
- This isn't something we plan to add, as we believe introspecting the DOM during generation is an anti-pattern.
- HTML DSL
- Explicit support for common html attributes in element functions for a cleaner api (i.e. id, classes, and attr)
- Lightweight streaming API
- Doesn't create a lot of unnecessary objects to represent the DOM. i.e. DOM introspection isn't possible
- Style DSL
- Allows inlining style to the header or individual elements
- CSS
- Multi-platform
- Integration with Ktor
- Simple Architecture - easy to understand and contribute to, especially relative to kotlinx.html
- If the HTML element or CSS Property you need doesn't exist, you can add it yourself via the generation module, or you can create an issue
- Please create an issue to discuss any major changes / refactors
There is a need for a simple Kotlin HTML templating DSL. kotlinx.html exposes an API that can be a little clunky at times, lacks features that many users need (i.e. styles), and lacks features that many users want. The community filed issues for some of these issues years ago, and no progress had been made. The values and goals of this library differentiate enough that it constituted a fresh start instead of attempting contribution.
dependencies {
implementation("dev.scottpierce.kotlin-html:kotlin-html-ktor:0.7.24")
}
You can use the extension function respondHtml
to write HTML as a response
fun Application.mainModule() {
routing {
get("ktor-sample") {
call.respondHtml {
head {
title("Ktor Sample")
metaDescription("This is a Ktor sample")
}
body {
+"Ktor Sample"
}
}
}
}
}
<Insert obligatory disclaimer about microbenchmarks here>
kotlin-html (this library) averaged 457 millis to create a page of ~7700 characters 100k times, and kotlinx.html stream averaged 1186 millis to create the same page page 100k times. That means that kotlin-html enjoys 2.6X performance improvement over kotlinx.html in this test case.
The test was performed by creating the same html page in kotlin-html and kotlinx.html, and then running the page creation 100k times. Each test was run 50 times for warmup, and then 50 iterations to get an average. The reality is that both of the libraries are fast enough that performance doesn't really matter for a normal use-case.
See the Benchmark Code
raw benchmark data
Benchmark Device:
JDK 12.0.1 2018 MacBook Pro 15" 2.9 GHz Intel Core i9 32 GB Memory