Managing Complexity

Composable components help manage software complexity by allowing developers to break down a complex problem into smaller, more manageable pieces. Other benefits include reusability, testability, and the ability to reason about a system in isolation.

The Component typealias

We rely on a small amount of syntactic sugar defined in kweb.components.Component:

typealias Component = ElementCreator<*>

And then we can use an extension function on this to create a component:

fun Component.simple(
    prompt: String = "Enter Your Name",
    name: KVar<String>
) {
    div {
        input(type = text).value = name
    div {
        span().text( { "Hello, $it" })

And we use the component like this:

    Kweb(port = 16097) {
        doc.body {
            simple(name = kvar("World"))

Components are configured through the extension function parameters, typically through a mixture of:

Parameter TypeFor
KValsValues that can change but not be modified by the component
KVarsValues that can change or be modified by the component
ObservableListsLists of values that can change or be modified by the component
Double, String, etcValues that don't change

The simplest Component's may have no parameters at all, or just one or two, while the most complex might use a DSL builder.

A more complex example

In this example we create a Component that wraps an <input> element styled using the Bulma CSS framework:

interface BulmaClass {
    val cssClassName: String

enum class BulmaColor(override val cssClassName: String) : BulmaClass {
    PRIMARY("is-primary"), LINK("is-link"),
    INFO("is-info"), SUCCESS("is-success"),
    WARNING("is-warning"), DANGER("is-danger")

enum class BulmaSize(override val cssClassName: String) : BulmaClass {
    SMALL("is-small"), NORMAL("is-normal"),
    MEDIUM("is-medium"), LARGE("is-large")

enum class BulmaStyle(override val cssClassName: String) : BulmaClass {

enum class BulmaState(override val cssClassName: String) : BulmaClass {
    NORMAL("is-normal"), HOVER("is-hovered"),
    FOCUS("is-focused"), LOADING("is-loading"),

fun Component.bulmaInput(
    type: InputType,
    value: KVar<String>,
    vararg classes : KVal<out BulmaClass> = arrayOf(),

) {
    input(type = type) { inputElement ->
        var inputClassList: KVal<List<String>> = kval(listOf("input"))

        for (c in classes) {
            inputClassList += { listOf(it.cssClassName) }

        with(inputElement) {

            classes( { it.joinToString(" ") })

            this.value = value


This component can then be used like this:

val bulmaUrl = "".json

fun shouldWarn(username : String) =
    if (username.length < 5)

Kweb(port = 12354) {
    doc.head {
            attributes = mapOf(
                "rel" to "stylesheet".json,
                "href" to bulmaUrl

    doc.body {
        val username = kvar("")
        val color = { shouldWarn(it) }
        bulmaInput(type = text, value = username, color)