When working with HTML generation in Kotlin, developers often face a choice: write raw HTML as text or build it programmatically. Thanks to domain-specific languages (DSLs), Kotlin offers a clean, type-safe, and flexible way to construct HTML directly in code.
In this guide, we’ll explore how to use Kotlin’s internal DSLs — with examples from the kotlinx.html library — to generate HTML efficiently. We’ll also highlight why DSLs are more than just a convenience: they add type safety, readability, and maintainability to your codebase.
What Is an Internal DSL?
A Domain-Specific Language (DSL) is a mini-language tailored for a specific task. An internal DSL is built within an existing language (like Kotlin), leveraging its syntax and features to make the new language feel natural and intuitive.
For HTML, this means you can write Kotlin code that looks and feels like HTML, while still enjoying the benefits of the Kotlin compiler.
Example: Creating a Simple Table
Let’s start with a basic example using kotlinx.html:
import kotlinx.html.*
import kotlinx.html.stream.createHTML
fun createSimpleTable(): String = createHTML().table {
tr {
td { +"cell" }
}
}This generates the following HTML:
<table>
<tr>
<td>cell</td>
</tr>
</table>At first glance, this might seem like extra work compared to just writing raw HTML. But there are key advantages.
Why Build HTML with Kotlin Code Instead of Plain Text?
Here are the main reasons developers prefer DSLs for HTML generation:
Type Safety
- The compiler enforces proper nesting. For example, a
<td>can only appear inside a<tr>. If you misuse tags, your code won’t compile—catching errors early.
Dynamic Content Generation
- Because it’s Kotlin code, you can loop, conditionally render, or dynamically build elements.
Cleaner, More Expressive Code
- DSLs reduce boilerplate and improve readability. Your HTML structure is represented directly in Kotlin’s syntax.
Example: Building Dynamic HTML from Data
Here’s a slightly more advanced example where we generate a table dynamically from a Map:
import kotlinx.html.*
import kotlinx.html.stream.createHTML
fun createAnotherTable(): String = createHTML().table {
val numbers = mapOf(1 to "one", 2 to "two")
for ((num, string) in numbers) {
tr {
td { +"$num" }
td { +string }
}
}
}This produces:
<table>
<tr>
<td>1</td>
<td>one</td>
</tr>
<tr>
<td>2</td>
<td>two</td>
</tr>
</table>Instead of manually writing repetitive markup, the loop handles it for you — making the code concise, flexible, and maintainable.
Beyond HTML: DSLs for XML and More
Although our examples focus on HTML, the same approach applies to other structured languages like XML. Kotlin’s DSL capabilities make it easy to define grammars for different domains, enabling developers to build powerful abstractions across use cases.
The Key Feature: Lambdas with Receivers
The magic behind Kotlin DSLs lies in lambdas with receivers.
In the HTML DSL, when you call table { ... }, the table element acts as the receiver. This allows nested blocks like tr { ... } and td { ... } to access its scope directly, creating a natural, hierarchical structure that mirrors HTML itself.
This feature makes DSLs:
- Readable — code mirrors the structure of the output
- Maintainable — changes are easy to apply across structures
- Error-resistant — misuse of tags or nesting gets caught at compile-time
Conclusion
Using internal DSLs in Kotlin — like kotlinx.html—isn’t just about writing code that looks like HTML. It’s about writing safer, more maintainable, and dynamic code that can scale with your project.
Whether you’re generating HTML, XML, or custom structured data, DSLs provide a powerful tool in a Kotlin developer’s toolkit. By leveraging lambdas with receivers and the expressive power of Kotlin, you can create elegant solutions tailored to your domain.
FAQs
Q: What is an internal DSL in Kotlin?
An internal DSL is a domain-specific language built within Kotlin using its existing syntax and features — like lambdas with receivers — to create readable, specialized code for a specific purpose such as HTML generation.
Q: Why prefer Kotlin DSL over plain HTML text?
Kotlin DSLs provide compile-time safety, reduce markup errors, and allow you to use Kotlin’s control structures, making the HTML generation dynamic and robust.
Q: Can this approach be used for XML or other markup languages?
Yes, the same DSL principles apply to XML or similar hierarchical languages, making it easy to adapt the code for various structured content production.
Q: What are lambdas with receivers?
They are functions that have an implicit receiver object, allowing direct access to its members within the lambda, enabling clean DSL-like syntax.
