TornadoFX Logo


JavaFX Framework for Kotlin

Travis CI Maven Central Apache License

** Important: TornadoFX is not yet compatible with Java 9 **


  • Supports both MVC, MVP and their derivatives
  • Dependency injection
  • Type safe GUI builders
  • Type safe CSS builders
  • First class FXML support
  • Async task execution
  • EventBus with thread targeting
  • Hot reload of Views and Stylesheets
  • OSGi support
  • REST client with automatic JSON conversion
  • Zero config, no XML, no annotations

Important version note

TornadoFX requires Kotlin 1.1.2 and jvmTarget 1.8. Make sure you update your IDE plugins (Kotlin + TornadoFX).

After updating IntelliJ IDEA, make sure your Kotlin target version is 1.1 (Project Settings -> Modules -> Kotlin -> Language Version / API Version)

Remember to update your build system to configure the jvmTarget as well.

For Maven, you add the following configuration block to kotlin-maven-plugin:


For Gradle, it means configuring the kotlinOptions of the Kotlin compilation task:

compileKotlin {
    kotlinOptions.jvmTarget= "1.8"

Failing to do so will yield errors about the compiler not being able to inline certain calls.

You also need a full rebuild of your code after a version upgrade. If you run into trouble, try to clean caches and restart IDEA (File -> Invalidate caches / Restart).

Getting started

Generate a quickstart application with Maven

mvn archetype:generate -DarchetypeGroupId=no.tornado \
  -DarchetypeArtifactId=tornadofx-quickstart-archetype \

Add TornadoFX to your project




compile 'no.tornado:tornadofx:1.7.15'

Snapshots are published to Sonatype

Configure your build environment to use snapshots if you want to try out the latest features:


Snapshots are published every day at GMT 16:00 if there has been any changes.

What does it look like? (Code snippets)

Create a View

class HelloWorld : View() {
    override val root = hbox {
        label("Hello world")

Load the root node from HelloWorld.fxml and inject controls by fx:id

import javafx.scene.control.Label
import javafx.scene.layout.HBox
import tornadofx.*

class HelloWorld : View() {
    override val root: HBox by fxml()
    val myLabel: Label by fxid()
    init {
        myLabel.text = "Hello world"

Start your application and show the primary View and add a type safe stylesheet

import javafx.scene.text.FontWeight
import tornadofx.*

class HelloWorldApp : App(HelloWorld::class, Styles::class)

class Styles : Stylesheet() {
    init {
        label {
            fontSize = 20.px
            fontWeight = FontWeight.BOLD
            backgroundColor += c("#cecece")

Start app and load a type safe stylesheet

Use Type Safe Builders to quickly create complex user interfaces

class MyView : View() {
    private val persons = FXCollections.observableArrayList(
            Person(1, "Samantha Stuart", LocalDate.of(1981,12,4)),
            Person(2, "Tom Marks", LocalDate.of(2001,1,23)),
            Person(3, "Stuart Gills", LocalDate.of(1989,5,23)),
            Person(3, "Nicole Williams", LocalDate.of(1998,8,11))

    override val root = tableview(persons) {
        column("ID", Person::id)
        column("Name", Person::name)
        column("Birthday", Person::birthday)
        column("Age", Person::age)
        columnResizePolicy = SmartResize.POLICY


edvin by tornadofx

Create a Customer model object that can be converted to and from JSON and exposes both a JavaFX Property and getter/setter pairs:

import tornadofx.getValue
import tornadofx.setValue

class Customer : JsonModel {
    val idProperty = SimpleIntegerProperty()
    var id by idProperty

    val nameProperty = SimpleStringProperty()
    var name by nameProperty

    override fun updateModel(json: JsonObject) {
        with(json) {
            id = int("id") ?: 0
            name = string("name")

    override fun toJSON(json: JsonBuilder) {
        with(json) {
            add("id", id)
            add("name", name)

Create a controller which downloads a JSON list of customers with the REST api:

class HelloWorldController : Controller() {
    val api : Rest by inject()
    fun loadCustomers(): ObservableList<Customer> = 

Configure the REST API with a base URI and Basic Authentication:

with (api) {
    baseURI = ""
    setBasicAuth("user", "secret")

Load customers in the background and update a TableView on the UI thread:

runAsync {
} ui {
    customerTable.items = it

Load customers and apply to table declaratively:

customerTable.asyncItems { controller.loadCustomers() }

Define a type safe CSS stylesheet:

class Styles : Stylesheet() {
    companion object {
        // Define css classes
        val heading by cssclass()
        // Define colors
        val mainColor = c("#bdbd22")

    init {
        heading {
            textFill = mainColor
            fontSize = 20.px
            fontWeight = BOLD
        button {
            padding = box(10.px, 20.px)
            fontWeight = BOLD

        val flat = mixin {
            backgroundInsets += box(0.px)
            borderColor += box(Color.DARKGRAY)

        s(button, textInput) {

Create an HBox with a Label and a TextField with type safe builders:

hbox {
    label("Hello world") {
    textfield {
        promptText = "Enter your name"

Get and set per component configuration settings:

// set prefWidth from setting or default to 200.0
node.prefWidth(config.double("width", 200.0))

// set username and age, then save
with (config) {
    set("username", "john")
    set("age", 30)

Create a Fragment instead of a View. A Fragment is not a Singleton like View is, so you will create a new instance and you can reuse the Fragment in multiple ui locations simultaneously.

class MyFragment : Fragment() {
    override val root = hbox {

Open it in a Modal Window:


Lookup and embed a View inside another Pane in one go


Inject a View and embed inside another Pane

val myView: MyView by inject()
init {

Swap a View for another (change Scene root or embedded View)

button("Go to next page") {
    action {
        replaceWith(PageTwo::class, ViewTransition.Slide(0.3.seconds, Direction.LEFT)

Open a View in an internal window over the current scene graph

button("Open") {
    action {
compile "no.tornado:tornadofx:1.7.15"

Related Libraries


Lightweight JavaFX Framework for Kotlin

Last updated 3 mins ago


Java / JavaFX / Kotlin Game Library (Engine)

Last updated 3 mins ago