Getting Started
Set up a Kraft project and build your first component.
Prerequisites
- JDK 17+ — Kraft compiles Kotlin to JavaScript, but the build runs on the JVM
- Gradle — the build system (wrapper included in most projects)
1. Set up your Gradle project
In your build.gradle.kts:
plugins {
kotlin("multiplatform") version "2.3.10"
kotlin("plugin.serialization") version "2.3.10"
}
kotlin {
js {
browser {
testTask { }
}
binaries.executable()
}
sourceSets {
jsMain {
dependencies {
// Kraft core
implementation("io.peekandpoke.kraft:core:0.107.2")
// SemanticUI integration (optional but recommended)
implementation("io.peekandpoke.kraft:semanticui:0.107.2")
}
}
}
} 2. Create your entry point
Create src/jsMain/resources/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>My Kraft App</title>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.8.8/dist/semantic.min.css">
</head>
<body>
<div id="app"></div>
<script src="your-project-name.js"></script>
</body>
</html> 3. Write your first app
Create src/jsMain/kotlin/main.kt:
import io.peekandpoke.kraft.components.*
import io.peekandpoke.kraft.routing.Static
import io.peekandpoke.kraft.vdom.VDom
import io.peekandpoke.kraft.vdom.preact.PreactVDomEngine
import io.peekandpoke.ultra.semanticui.ui
import kotlinx.html.*
// Define the app and its routes
val app = kraftApp {
semanticUI()
routing {
mount(Static("")) { HomePage() }
}
}
// Entry point
fun main() {
app.mount("#app", PreactVDomEngine()) {
RouterComponent()
}
} 4. Create your first component
Create src/jsMain/kotlin/HomePage.kt:
import io.peekandpoke.kraft.components.*
import io.peekandpoke.kraft.vdom.VDom
import io.peekandpoke.ultra.semanticui.ui
import kotlinx.html.*
// Factory function — this is how other components use yours
@Suppress("FunctionName")
fun Tag.HomePage() = comp {
HomePage(it)
}
// The component class
class HomePage(ctx: NoProps) : PureComponent(ctx) {
// Reactive state — changing this redraws the component
private var count by value(0)
override fun VDom.render() {
ui.container {
ui.segment {
ui.header H1 { +"Hello from Kraft!" }
p { +"You clicked the button $count times." }
ui.blue.button {
onClick { count++ }
+"Click me"
}
}
}
}
} 5. Run it
./gradlew -t --parallel jsRun The -t flag enables continuous build — save a file and the browser refreshes.
What just happened?
Let's break down the patterns you just used:
The factory function pattern
@Suppress("FunctionName")
fun Tag.HomePage() = comp {
HomePage(it)
} Every component has a factory function — an extension on Tag that creates the component via comp { }. This is how you compose components in the HTML DSL:
override fun VDom.render() {
div {
HomePage() // Just call the factory function
Counter(start = 5) // With props
}
} Reactive state with value()
private var count by value(0) value() creates a delegated property that automatically triggers a redraw when changed. No setState(), no actions, no dispatchers — just assign a new value.
The HTML DSL
ui.container {
ui.segment {
ui.header H1 { +"Hello" }
}
} This is kotlinx.html with the
SemanticUI DSL layered on top. Standard HTML tags (div, p, button) work
alongside SemanticUI helpers (ui.segment, ui.button).
App configuration
The kraftApp builder is where you configure routing, UI frameworks, and app-level services. Here's a
complete setup:
val app = kraftApp {
// Registers modals, toasts, popups with SemanticUI styling
semanticUI {
// Optional: customize toast defaults
defaultDuration = 5.seconds
}
// Set up routing
routing {
usePathStrategy() // clean URLs: /users/123
// or useHashStrategy() for: /#/users/123
mount(Nav.home) { HomePage() }
mount(Nav.users) { route -> UsersPage(route["id"]) }
catchAll { NotFoundPage() }
}
}
fun main() {
app.mount("#spa", PreactVDomEngine()) {
App() // your root component
}
} The semanticUI() call registers modals, toasts, and popups with FomanticUI styling. If you want the
core overlay systems without SemanticUI, register them individually with modals(),
toasts(), and popups().