Kotlin/JS is a Kotlin-to-JavaScript compiler that allows creating frontend applications and web interfaces in the Kotlin language. Unlike TypeScript, Kotlin/JS provides strict static typing, coroutine support, and seamless integration with Kotlin Multiplatform. Since Kotlin 1.8, the compiler uses an IR-based backend to generate optimized JavaScript code. According to JetBrains, 2025, Kotlin/JS supports integration with the NPM ecosystem and TypeScript through automatic .d.ts file generation.
Key Takeaways
Kotlin/JS is one of the three Kotlin compiler backends (alongside JVM and Native), designed for translating Kotlin code into JavaScript. Developed by JetBrains, Kotlin/JS allows writing web applications in a language with strict static typing while retaining access to the vast JavaScript library ecosystem through external declarations. Since the first stable release in Kotlin 1.3, the compiler has evolved from an experimental backend to a production-ready tool used in industrial projects.
The main purpose of Kotlin/JS is frontend development using Kotlin instead of JavaScript or TypeScript. Developers get all the features of the Kotlin language: null safety, extension functions, data classes, coroutines, and sealed classes. The generated JavaScript code can run in any modern browser or in a Node.js environment. Kotlin/JS is especially valuable in Kotlin Multiplatform projects, where the shared module (commonMain) is available for both mobile and web applications.
The Kotlin/JS ecosystem includes frameworks for building user interfaces. Compose Multiplatform for web uses Kotlin/JS as its target backend, enabling declarative UI components that run in the browser. Alternative solutions include wrappers for React — Kotlin Wrappers, which provide a type-safe API for React, React Router, and Redux.
Compilation in Kotlin/JS goes through several transformation stages. The source Kotlin code is first compiled into an intermediate representation (IR), then optimized, and finally JavaScript code is generated. Since Kotlin 1.8, the old backend (JsBackend based on AST) has been completely replaced by the IR backend, which provides better optimization, smaller output code size, and TypeScript compatibility.
Previously, Kotlin/JS used JsBackend, which worked directly with the AST (Abstract Syntax Tree). This approach had limitations: difficulty supporting platform optimizations, lack of a unified IR for all backends, and inability to generate TypeScript declarations. The transition to the IR backend solved these problems, unifying compilation across all three Kotlin backends and opening the path for cross-backend optimizations.
Kotlin/JS supports two build modes that determine the format and optimization of the output code. Production mode uses Webpack for bundling, minification, and tree-shaking. Development mode supports Hot Module Replacement for rapid development iteration. The mode selection is configured in build.gradle.kts through the js.target configuration.
kotlin {
js {
browser {
webpackTask {
outputFileName = "app.js"
}
}
binaries.executable()
}
}
Integration of Kotlin/JS with the NPM ecosystem is implemented through a special DSL in build.gradle.kts. The developer declares npm dependencies in the kotlin configuration, specifying the package name and version. Kotlin/JS automatically fetches packages via the npm or yarn package manager. To ensure type-safe access to JavaScript libraries, external declarations are used, which describe the library types and functions for the Kotlin compiler.
External declarations are Kotlin declarations marked with the external keyword. They tell the compiler that the function or class implementation is in JavaScript code, not in Kotlin. JetBrains provides ready-made wrapper sets for popular libraries as part of Kotlin Wrappers, including React, React Router, Redux, Emotion, and MUI. For libraries without ready wrappers, the developer can write external declarations manually or use the Dukat TypeScript binding generator.
kotlin {
js {
browser { }
npm {
dependency("react", "18.3.1")
dependency("react-dom", "18.3.1")
devDependency("webpack", "5.92.0")
}
}
}
// External declaration for JavaScript library
@JsModule("date-fns")
external fun formatDistance(
date: Date, baseDate: Date
): String
TypeScript is an integral part of the modern web, and Kotlin/JS ensures compatibility with it through .d.ts file generation. When js.generateTsDeclarations = true is set, the compiler creates TypeScript declarations for all public Kotlin functions and classes. This allows TypeScript projects to import Kotlin modules with full type support. TypeScript declaration generation only works with the IR backend and is one of the key reasons for migrating from the old backend.
IR Backend (Intermediate Representation) is a unified backend for all three Kotlin platforms: JVM, JS, and Native. It uses a common intermediate representation that goes through several optimization stages before generating the target code. For Kotlin/JS, the IR backend became mandatory starting with Kotlin 1.8.0, replacing the old AST-based backend, which was marked as deprecated.
The advantages of the IR backend for Kotlin/JS include improved code optimization at the IR level, reduced bundle size through dead code elimination and inline optimizations, as well as support for TypeScript declarations. The IR backend also ensures correct handling of inline functions, reified generics, and suspend functions when translating to JavaScript, which was problematic in the old backend.
| Feature | Old Backend (AST) | IR Backend |
|---|---|---|
| TypeScript Declarations | Not supported | Generated automatically |
| Dead Code Elimination | Limited | Full, at IR level |
| Inline Functions | Correctness issues | Correct handling |
| Reified Generics | Not supported | Supported |
| Bundle Size | Baseline | 20–30% smaller |
A full Kotlin/JS project starts with configuring build.gradle.kts. The kotlin multiplatform plugin allows setting up Kotlin/JS as one of the target platforms. Let us look at an example setup for a web application with React.
plugins {
kotlin("multiplatform") version "2.0.21"
id("org.jetbrains.kotlin-wrappers") version "1.0.0-pre.775"
}
kotlin {
js {
browser { }
binaries.executable()
generateTsDeclarations = true
}
}
dependencies {
implementation(enwraps(react, reactDom))
}
After configuration, you can write React components in Kotlin. Kotlin Wrappers provide a type-safe API for React, including functional components, hooks, and JSX-like syntax through DSL builders. The Counter component example demonstrates working with useState and event handlers.
@OptIn(ExperimentalWrapperApi::class)
val Counter = FC<Props> {
var count = useState(0)
div {
+"Counter value: ${count.component1()}"
button {
onClick = { count.component2()(count.component1() + 1) }
+"Increment"
}
}
}
fun main() {
createRoot(document.getElementById("root")!!).render(
createElement(Counter)
)
}
Building a Kotlin/JS project is done through Gradle tasks. jsDevelopmentRun starts the Webpack dev server with HMR for development. jsProductionExecutableCompileSync generates a production bundle with minification and tree-shaking. The final HTML file that includes the compiled JavaScript is generated by the webpack plugin and placed in build/dist.
// Start dev server with HMR
./gradlew jsDevelopmentRun
// Production build with minification
./gradlew jsProductionExecutableCompileSync
// Output in build/dist/productionExecutable/
TypeScript is the main competitor to Kotlin/JS in the typed JavaScript space. Both languages compile to JavaScript and add static typing on top of dynamic JS. However, Kotlin/JS offers several advantages: null safety by default, sealed classes, coroutines, and seamless integration with Kotlin Multiplatform. TypeScript, on the other hand, has a larger community, more ready-made libraries with types, and a more mature ecosystem.
Development productivity with Kotlin/JS may be higher for teams already using Kotlin in other parts of the project, as no language switching is required. TypeScript remains the more popular choice for pure web projects where Kotlin Multiplatform is not used. However, with the growing popularity of Compose Multiplatform for web, Kotlin/JS is gaining ground as an alternative to React and Angular.
| Feature | Kotlin/JS | TypeScript |
|---|---|---|
| Null Safety | By default | strictNullChecks optional |
| Sealed Classes | Supported | Via union types |
| Coroutines | Natively | Via async/await |
| KMM Integration | Full | None |
| Community Size | Small | Huge |
| Ready Types for Libraries | Limited (DefinitelyTyped via Dukat) | DefinitelyTyped (thousands of libraries) |
The choice between Kotlin/JS and TypeScript depends on the project context. If the team already uses Kotlin Multiplatform or plans to start, Kotlin/JS is a logical choice for the frontend part. For an isolated web project, TypeScript remains a more pragmatic solution due to the availability of ready types for JavaScript libraries and a larger pool of developers on the market.
Frequently Asked Questions
Yes, JetBrains provides Kotlin Wrappers — type-safe Kotlin wrappers for React, React Router, and Redux. They allow writing React components in Kotlin with full support for hooks and JSX-like DSL.
When generateTsDeclarations = true is enabled, Kotlin/JS generates .d.ts files for all public APIs. TypeScript projects can import Kotlin modules with full typing as regular npm packages.
The bundle size depends on the libraries used. A minimal Hello World application takes about 300 KB including the Kotlin stdlib. With React and Kotlin Wrappers, the size increases to 400–600 KB.
Yes, Kotlin/JS can be used for developing serverless functions and console utilities under Node.js. To do this, specify js.nodejs instead of js.browser in the configuration.
Kotlin/Wasm is an experimental backend for WebAssembly, while Kotlin/JS generates JavaScript code. Wasm provides higher performance but has limited access to the browser DOM.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also