skip to content

Kotlin/JS (IR) Target

The JS target compiles through the IR backend to JavaScript, targeting the browser or Node, emitting ES modules and eliminating dead code from the stdlib. Bundle size is the practical concern interviewers raise.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What is the Kotlin/JS IR target, and how do you enable it in a Gradle build with browser() and nodejs() environments?

level: juniorimportance: must knowfreq 55%

answer

  1. IR = Intermediate Representation backend
  2. Only backend since Kotlin 1.8 (legacy removed)
  3. js(IR) { browser() / nodejs() }
  4. binaries.executable() = runnable output
  5. browser=DOM+webpack, nodejs=Node runtime

basics

~10 s

Kotlin/JS lets you compile Kotlin code into JavaScript. The IR target is the modern compiler backend. In Gradle you add js(IR) and pick browser() or nodejs() depending on where the code runs.

solid answer

~40 s

Kotlin/JS compiles Kotlin to JavaScript so the same language runs in browsers or Node. The IR (Intermediate Representation) backend is the modern, only-supported compiler since Kotlin 1.8; the old 'legacy' backend was removed. You declare the target in the Kotlin Gradle DSL with kotlin { js(IR) { ... } }, then choose an environment: browser() configures a webpack-based dev server and bundling for the DOM, while nodejs() targets the Node.js runtime. You can declare both. binaries.executable() tells the compiler to emit a runnable artifact rather than just a library. The IR backend enables features the legacy one couldn't: efficient Dead Code Elimination, ES-module output, and TypeScript declaration generation via generateTypeScriptDefinitions().

code

kotlin · 7 lines
kotlin
kotlin {
    js(IR) {
        browser()
        nodejs()
        binaries.executable()
    }
}

go deeper

for a junior

Knows Kotlin/JS compiles Kotlin to JavaScript and can name js(IR) with browser/nodejs.

for a middle

Explains IR is the only backend since 1.8 and the role of binaries.executable() and environment choice.

for a senior

Connects the IR backend to downstream capabilities (DCE, ES modules, .d.ts) and configures both environments deliberately.

for a principal

Reasons about migration from legacy, build-tooling implications, and when a klib-only vs executable output is the right module boundary.

## What Kotlin/JS is Kotlin/JS is a compilation target that translates Kotlin source into JavaScript, letting you share Kotlin logic with web frontends, Node services, or npm packages. It is one of several targets in a Kotlin Multiplatform (KMP) project alongside JVM, Native, and Wasm. ## What 'IR' means IR stands for **Intermediate Representation** — a compiler-internal tree the frontend produces before generating output. Kotlin originally had a 'legacy' JS backend that translated straight to JS. The **IR backend** instead lowers code through the same shared IR pipeline used by Kotlin/Native, which unlocks better optimization. Since **Kotlin 1.8** the IR backend is the only one; `js(LEGACY)` and the mixed `js(BOTH)` mode were removed. So today `js(IR)` and plain `js` are equivalent. ## Enabling it in Gradle You configure targets inside the `kotlin { }` block of the Kotlin Multiplatform Gradle plugin. ```kotlin kotlin { js(IR) { browser { // DOM / webpack environment commonWebpackConfig { cssSupport { enabled.set(true) } } } nodejs() // Node.js environment (can coexist) binaries.executable() // emit a runnable bundle, not just a klib } } ``` ## browser() vs nodejs() - **browser()** wires in a webpack-backed dev server (`browserDevelopmentRun`), production/development bundling, and assumes a DOM. Use it for web apps. - **nodejs()** targets the Node runtime, gives you `nodeRun`/test tasks, and assumes Node globals (no DOM). Use it for CLIs, scripts, or server code. - You may declare both; each gets its own runtime-specific tasks. ## binaries.executable() Without it, the target builds a **klib** (Kotlin library) only. With it, the compiler emits an actual JavaScript entry artifact you can run or ship. ## Why it matters The IR backend is the foundation for the other features on this topic: tree-shaking via Dead Code Elimination, ES-module output, and TypeScript `.d.ts` generation.

  • If you write plain js {} instead of js(IR) {}, which backend do you get today?
    The IR backend. Since Kotlin 1.8 it's the only one, so js and js(IR) are equivalent; the explicit IR argument is now redundant but still accepted.
  • What is produced if you omit binaries.executable()?
    Only a klib library artifact, not a runnable JS bundle. You'd use that when the module is consumed by another Kotlin target rather than run directly.

The IR backend is like a shared assembly line: JS, Native, and Wasm all feed the same optimizing machinery instead of each having a bespoke conveyor belt.

saying these in an interview costs you the question

  • Thinking the legacy backend is still selectable or default
  • Believing browser() and nodejs() are mutually exclusive
  • Confusing IR (compiler representation) with a JS framework
  • Forgetting binaries.executable() and wondering why nothing runs
  • Claiming Kotlin/JS produces WebAssembly (that's the Wasm target)

context

open as a page

How does Dead Code Elimination work in the Kotlin/JS IR backend, and how does it affect bundle size of kotlin-stdlib-js?

level: middleimportance: should knowfreq 45%

basics

~10 s

Dead Code Elimination removes Kotlin code your app never uses from the final JavaScript. That keeps the bundle small, so you don't ship the whole standard library when you only call a few functions.

open as a page

How do you make the Kotlin/JS IR target emit ES modules, and what does that change about the output and interop?

level: seniorimportance: should knowfreq 38%

basics

~10 s

You tell the compiler to output ES modules instead of the default UMD format. Then the generated JavaScript uses import/export statements, so modern browsers and tools can load and tree-shake it natively.

open as a page

Explain @JsExport and how Kotlin types are exposed to JavaScript/TypeScript from the IR backend. What are the limits?

level: seniorimportance: should knowfreq 32%

basics

~10 s

@JsExport marks Kotlin declarations so they keep readable names and become callable from JavaScript. The IR backend can also generate TypeScript type files for them, but only certain Kotlin types map cleanly.

open as a page

Your team is migrating a Kotlin/JS app from the legacy backend to IR and bundle size unexpectedly grew. How do you reason about and resolve this?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Check that you measured a production build, look for broad @JsExport that blocks code removal, confirm the right module format, and verify your JS interop and npm dependencies still tree-shake. The IR backend usually shrinks bundles, so growth signals a misconfiguration.

open as a page