Contents

Pebble templating vs. HTML DSL in Ktor

Pebble templating vs. HTML DSL in Ktor webp image

Server Side Rendering (SSR) has made a big comeback in recent months. It’s been quite refreshing to take a step back and not use JSON for communication with the front-end. There are quite a few templating languages to choose from and if you know one, switching between them is usually straightforward. Recently, I’ve discovered one that I wanted to check out - HTML DSL, which is a kotlinx.html integration that is bundled with Ktor, a Kotlin framework for building asynchronous client and server applications. It is a different approach to templating because with it, you can write pure HTML in Kotlin, interpolate variables into views, and build complex layouts. In my opinion, the extensibility of HTML is one of its strongest suits, and I’m not sure if type-safety will play well with it, but let’s deep dive into that and compare HTML DSL to Pebble templates, which is a more traditional templating language!

Base page with Pebble

I’ve envisioned comparing the two on an example rolodex app for managing contacts built with Pico for some lightweight styles, but let’s take it step by step and start with nothing meaningful inside of <body>. The pebble template is identical to pure HTML in this case:

<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
  <link rel="stylesheet" href="/static/css/main.css">
  <meta charset="UTF-8"/>
  <title>Pebble vs HTML DSL</title>
</head>
<body>
<main class="container">
  <h1>It works 🚀!</h1>
</main>
</body>
</html>

It looks pretty standard, but there is a custom argument data-themein <html> tag from Pico.

The next step would incorporate extracting the base page. Some sort of inheritance or composition is an essential feature when building a HTML page server side - it can be used to extract a portion of the page, and the actual content is later injected into it. When using Pebble, this is done through tags (different from standard HTML tags):

{# base.peb #}

<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
  <link rel="stylesheet" href="/static/css/main.css">
  <meta charset="UTF-8"/>
  <title>Pebble vs HTML DSL</title>
  <style>
  </style>
</head>
<body>
<main class="container">
    {% block content %}{% endblock %}
</main>
</body>
</html>

And then we can reuse it to render index page like so:

{# index.peb #}
{% extends "base.peb" %}

{% block content %}
<h1>It works 🚀!</h1>
{% endblock %}

Base page with HTML DSL and descoping

Having some intuition about the base page in standard templating language we can jump into HTML DSL. After reading the docs and doing some research on custom parameters Kotlin code in routing that corresponds to the basic index.peb looks like this:

fun Application.configureRouting() {
  routing { 
    staticResources("/static", "static") {
      extensions("css")
      preCompressed(CompressedFileType.GZIP)
    }
    get("/") {
      call.respondHtml(HttpStatusCode.OK) {
        lang = "en"
        attributes["data-theme"] = "light"
        head {
          link {
            href = "https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
            rel = "stylesheet"
          }
          link {
            href = "/static/css/main.css"
            rel = "stylesheet"
          }
          meta {
            charset = "UTF-8"
          }
          title {
            +"Pebble vs HTML DSL"
          }
        }
        body {
          main {
            classes = linkedSetOf("container")
            h1 {
              +"It works 🚀!"
            }
          }
        }
      }
    }
  }
}

This looks completely different from standard HTML! But is it a good thing? In my honest opinion, no, it is not and these are the issues I have with the DSL approach:

  • Even with a page as simple as this one, it isn’t immediately obvious which parts of the code map to which sections of the HTML. This might make debugging JavaScript code more challenging than necessary.
  • Because the code doesn’t resemble HTML, it will be difficult for those who aren’t accustomed to Kotlin to work on the front-end portion of the project.
  • This dissimilarity has another disadvantage - component libraries built for HTML are not usable in this approach without non trivial amount of work poured into translating them to the DSL
  • While using a Set for class attribute seems like a good idea to get rid of duplication, it might have some unintended consequences, because it can break your attribute selectors, e.g. [class="class1 class2"] is not the same as the [class="class2 class1"]. In general, we should use linkedSetOf
  • As said in the beginning, one of the design principles of HTML is extensibility.I was skeptical that strict type-safety could be maintained in all cases and sure enough, it broke in the first tag when we wanted to adjust the theme of Pico with data attribute. It’s also quite common for libraries to use other custom attributes.

Now let’s try to refactor out the base part of this code into a separate method of HTML class, so it would be a part of our DSL.

object Templates {
    fun HTML.base(content: FlowContent.() -> Unit) {
        lang = "en"
        attributes["data-theme"] = "light"

        ...

        body {
            main {
                classes = setOf("container")
                content()
            }
        }
    }
}

//Application.configureRouting
get("/") {
  call.respondHtml(HttpStatusCode.OK) {
    base {
      h1 {
        +"It works 🚀!"
      }
    }
  }
}

This refactor needed some more Kotlin knowledge, but it is a work that should not vary much from case to case.

When I started to write this article, I had a really grand plan in mind - a comparison of Pebble and HTML DSL based on a simple rolodex app, unfortunately the DSL made even a read-only app quite unreadable, and I decided not to go through with the rest in this post. That being said, all the code for the finished read-only version is available on this Github repository and when it comes to the lines of code it’s 75 vs 128 with fewer lines being Pebble.

Conclusion

There are a few advantages to the HTML DSL that I can think of:

  • First-class support of IDEA, because it’s just a Kotlin code
  • Being the only option of templating in Ktor that can be used with a native server 
  • It works immediately after adding a dependency to the build
  • It doesn’t have irritating limitation of tags in templates, which should usually be placed in a single line

But personally I won’t be using it again anytime soon - in my opinion it makes the development of web apps more complicated than it should be in comparison with established template languages.

Reviewed by: Łukasz Lenart

Blog Comments powered by Disqus.