Skip to main content

Kotlin JS Development

Introduction

Kotlin/JS is a powerful technology that allows developers to write Kotlin code that transpiles to JavaScript. As part of the Kotlin Multiplatform ecosystem, it enables developers to share code between different platforms while still leveraging platform-specific capabilities. With Kotlin/JS, you can build web applications, utilize JavaScript libraries, and even share code with Android, iOS, or backend applications.

In this guide, we'll explore the fundamentals of Kotlin/JS development, from setting up your environment to creating interactive web applications and integrating with JavaScript libraries.

Setting Up Your Environment

Before diving into Kotlin/JS development, you'll need to set up your development environment.

Prerequisites

  • JDK 8 or later
  • IntelliJ IDEA (recommended) or any IDE with Kotlin support
  • Gradle or Maven build system

Creating a New Kotlin/JS Project

The easiest way to create a new Kotlin/JS project is through IntelliJ IDEA:

  1. Open IntelliJ IDEA
  2. Select "New Project"
  3. Choose "Kotlin" from the left panel
  4. Select "Kotlin/JS for Web" or "Kotlin/JS for Node.js" template
  5. Configure your project settings and click "Finish"

Alternatively, you can set up a project using Gradle. Create a build.gradle.kts file with the following content:

kotlin
plugins {
kotlin("js") version "1.8.0"
}

repositories {
mavenCentral()
}

kotlin {
js(IR) {
browser {
commonWebpackConfig {
cssSupport {
enabled.set(true)
}
}
binaries.executable()
}
}
sourceSets {
val main by getting {
dependencies {
implementation("org.jetbrains.kotlin-wrappers:kotlin-react:18.2.0-pre.467")
implementation("org.jetbrains.kotlin-wrappers:kotlin-react-dom:18.2.0-pre.467")
}
}
}
}

Your First Kotlin/JS Program

Let's start with a simple "Hello World" program in Kotlin/JS. Create a file named App.kt in the src/main/kotlin directory:

kotlin
import kotlinx.browser.document

fun main() {
document.addEventListener("DOMContentLoaded", {
val message = document.createElement("h1")
message.textContent = "Hello, Kotlin/JS!"
document.body?.appendChild(message)
})
}

When executed in a browser, this code will create an <h1> element with the text "Hello, Kotlin/JS!" and append it to the document body.

Running the Application

To run your application:

  1. Execute the ./gradlew browserDevelopmentRun command (for Gradle)
  2. Open your browser at http://localhost:8080 (default port)

You should see "Hello, Kotlin/JS!" displayed in your browser.

DOM Manipulation with Kotlin/JS

Kotlin/JS provides access to the browser's DOM API, allowing you to manipulate web pages programmatically.

Example: Creating a Dynamic List

Here's an example of creating a dynamic list using Kotlin/JS:

kotlin
import kotlinx.browser.document
import org.w3c.dom.HTMLLIElement
import org.w3c.dom.HTMLUListElement

fun main() {
document.addEventListener("DOMContentLoaded", {
// Create title
val title = document.createElement("h2")
title.textContent = "My Favorite Programming Languages"
document.body?.appendChild(title)

// Create list
val languageList = document.createElement("ul") as HTMLUListElement
document.body?.appendChild(languageList)

// Add items to list
val languages = listOf("Kotlin", "JavaScript", "Swift", "Python", "Rust")

languages.forEach { language ->
val listItem = document.createElement("li") as HTMLLIElement
listItem.textContent = language
languageList.appendChild(listItem)
}
})
}

This code will generate a heading followed by a bullet list of programming languages.

Event Handling

Handling events is crucial for interactive web applications. Here's how you can add event listeners in Kotlin/JS:

kotlin
import kotlinx.browser.document
import org.w3c.dom.HTMLButtonElement
import org.w3c.dom.HTMLDivElement
import org.w3c.dom.events.Event

fun main() {
document.addEventListener("DOMContentLoaded", {
val button = document.createElement("button") as HTMLButtonElement
button.textContent = "Click me!"
document.body?.appendChild(button)

val output = document.createElement("div") as HTMLDivElement
document.body?.appendChild(output)

var clickCount = 0

button.addEventListener("click", { _: Event ->
clickCount++
output.textContent = "Button clicked $clickCount time(s)!"
})
})
}

When you click the button, the text will update to show how many times the button has been clicked.

Working with JavaScript Libraries

One of the biggest advantages of Kotlin/JS is the ability to use existing JavaScript libraries. You can do this through:

  1. Dynamic typing - Quick but unsafe access to JS libraries
  2. Kotlin wrappers - Type-safe Kotlin APIs for popular JS libraries
  3. Creating your own typings - For libraries without existing wrappers

Using Dynamic Type

Kotlin provides the dynamic type for interacting with JavaScript libraries without type checking:

kotlin
import kotlinx.browser.window

fun main() {
// Accessing a hypothetical JS library without typings
val jsLib = js("MyJSLibrary")

// Calling methods on the JS library
jsLib.initialize()

// Working with JS objects
val person = js("{ name: 'John', age: 30 }")
window.alert("Hello, ${person.name}!")
}

Using Kotlin Wrappers

For popular libraries like React, Kotlin provides official wrappers that offer type-safe APIs:

kotlin
import react.*
import react.dom.*
import kotlinx.browser.document

external interface WelcomeProps : Props {
var name: String
}

val Welcome = FC<WelcomeProps> { props ->
h1 {
+"Hello, ${props.name}!"
}
}

fun main() {
val container = document.createElement("div")
document.body!!.appendChild(container)

createRoot(container).render(Fragment.create {
Welcome {
name = "Kotlin Developer"
}
})
}

Building a Real-World Application: To-Do List

Let's build a simple to-do list application to demonstrate Kotlin/JS in action:

kotlin
import kotlinx.browser.document
import kotlinx.browser.window
import org.w3c.dom.HTMLButtonElement
import org.w3c.dom.HTMLInputElement
import org.w3c.dom.HTMLLIElement
import org.w3c.dom.HTMLUListElement
import org.w3c.dom.events.Event

data class TodoItem(val text: String, var completed: Boolean = false)

fun main() {
document.addEventListener("DOMContentLoaded", {
setupTodoApp()
})
}

fun setupTodoApp() {
// Create app elements
val heading = document.createElement("h1")
heading.textContent = "Kotlin/JS Todo App"
document.body?.appendChild(heading)

val inputContainer = document.createElement("div")
document.body?.appendChild(inputContainer)

val input = document.createElement("input") as HTMLInputElement
input.setAttribute("type", "text")
input.setAttribute("placeholder", "Enter a task...")
inputContainer.appendChild(input)

val addButton = document.createElement("button") as HTMLButtonElement
addButton.textContent = "Add Task"
inputContainer.appendChild(addButton)

val todoList = document.createElement("ul") as HTMLUListElement
document.body?.appendChild(todoList)

val todos = mutableListOf<TodoItem>()

// Handle add button click
addButton.addEventListener("click", {
val text = input.value.trim()
if (text.isNotEmpty()) {
todos.add(TodoItem(text))
renderTodos(todoList, todos)
input.value = ""
}
})
}

fun renderTodos(todoList: HTMLUListElement, todos: List<TodoItem>) {
// Clear existing items
while (todoList.firstChild != null) {
todoList.removeChild(todoList.firstChild!!)
}

// Render updated list
todos.forEachIndexed { index, todo ->
val item = document.createElement("li") as HTMLLIElement

val checkbox = document.createElement("input") as HTMLInputElement
checkbox.setAttribute("type", "checkbox")
checkbox.checked = todo.completed

checkbox.addEventListener("change", {
todos[index].completed = checkbox.checked
renderTodos(todoList, todos)
})

item.appendChild(checkbox)

val textSpan = document.createElement("span")
textSpan.textContent = todo.text
if (todo.completed) {
textSpan.style.textDecoration = "line-through"
}
item.appendChild(textSpan)

val deleteButton = document.createElement("button") as HTMLButtonElement
deleteButton.textContent = "Delete"
deleteButton.addEventListener("click", {
todos.removeAt(index)
renderTodos(todoList, todos)
})
item.appendChild(deleteButton)

todoList.appendChild(item)
}
}

This application allows you to add tasks to a list, mark them as complete, and delete them.

Building and Deploying Kotlin/JS Applications

When you're ready to deploy your Kotlin/JS application, you'll need to build it for production.

Production Build

For Gradle projects, run:

bash
./gradlew browserProductionWebpack

This will generate optimized JavaScript files in the build/distributions directory.

Deployment Options

You can deploy your Kotlin/JS application like any other JavaScript application:

  1. Static hosting services (GitHub Pages, Netlify, Vercel)
  2. Traditional web servers (Apache, Nginx)
  3. Cloud platforms (AWS, Google Cloud, Azure)

Simply upload the contents of the build/distributions directory to your hosting provider.

Summary

In this guide, we've covered:

  • Setting up a Kotlin/JS development environment
  • Creating basic web applications with Kotlin/JS
  • DOM manipulation and event handling
  • Working with JavaScript libraries
  • Building a practical to-do list application
  • Building and deploying Kotlin/JS applications

Kotlin/JS offers a powerful way to build web applications using the Kotlin language you may already know from Android or backend development. By leveraging Kotlin Multiplatform, you can share code between your web frontend and other platforms, significantly reducing development time and effort.

Additional Resources

Exercises

  1. Extend the to-do list application to store tasks in the browser's localStorage
  2. Create a simple calculator application using Kotlin/JS
  3. Build a weather application that fetches data from a public API
  4. Convert an existing JavaScript application to Kotlin/JS
  5. Create a Kotlin/JS wrapper for a small JavaScript library


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)