Kotlin Help

Use Kotlin code from JavaScript

Depending on the selected JavaScript Module system, the Kotlin/JS compiler generates different output. But in general, the Kotlin compiler generates normal JavaScript classes, functions and properties, which you can freely use from JavaScript code. There are some subtle things you should remember, though.

Isolating declarations in a separate JavaScript object in plain mode

If you have explicitly set your module kind to be plain, Kotlin creates an object that contains all Kotlin declarations from the current module. This is done to prevent spoiling the global object. This means that for a module myModule, all declarations are available to JavaScript via the myModule object. For example:

fun foo() = "Hello"

This function can be called from JavaScript like this:

alert(myModule.foo());

Calling the function directly like this is not applicable when you compile your Kotlin module to JavaScript modules like UMD (the default setting for both browser and nodejs targets), ESM, CommonJS, or AMD. In these cases, your declarations are exposed according to the chosen JavaScript module system. For example, when using UMD, ESM, or CommonJS, your call site would look like this:

alert(require('myModule').foo());

For more information about JavaScript module systems, see JavaScript Modules.

Package structure

For most of the module systems (CommonJS, Plain, and UMD), Kotlin exposes its package structure to JavaScript. Unless you define your declarations in the root package, you have to use fully qualified names in JavaScript. For example:

package my.qualified.packagename fun foo() = "Hello"

For example, when using UMD or CommonJS, your call site could look like this:

alert(require('myModule').my.qualified.packagename.foo())

When using plain as a module system setting, the call site would be:

alert(myModule.my.qualified.packagename.foo());

When targeting ECMAScript Modules (ESM), package information is not preserved to improve the application bundle size and match the typical layout of ESM packages. In this case, the consumption of the Kotlin declarations with ES modules looks like this:

import { foo } from 'myModule'; alert(foo());

@JsName annotation

In some cases (for example, to support overloads), the Kotlin compiler mangles the names of generated functions and attributes in JavaScript code. To control the generated names, you can use the @JsName annotation:

// Module 'kjs' class Person(val name: String) { fun hello() { println("Hello $name!") } @JsName("helloWithGreeting") fun hello(greeting: String) { println("$greeting $name!") } }

Now you can use this class from JavaScript in the following way:

// If necessary, import 'kjs' according to chosen module system var person = new kjs.Person("Dmitry"); // refers to module 'kjs' person.hello(); // prints "Hello Dmitry!" person.helloWithGreeting("Servus"); // prints "Servus Dmitry!"

If we didn't specify the @JsName annotation, the name of the corresponding function would contain a suffix calculated from the function signature, for example hello_61zpoe$.

Note that there are some cases in which the Kotlin compiler does not apply mangling:

  • external declarations are not mangled.

  • Any overridden functions in non-external classes inheriting from external classes are not mangled.

The parameter of @JsName is required to be a constant string literal which is a valid identifier. The compiler will report an error on any attempt to pass non-identifier string to @JsName. The following example produces a compile-time error:

@JsName("new C()") // error here external fun newC()

@JsExport annotation

By applying the @JsExport annotation to a top-level declaration (like a class, interface, or function), you can make Kotlin declarations available from JavaScript or TypeScript. The annotation exports all nested declarations with the name given in Kotlin.

For example, here's how you can export a Kotlin interface with a nested class and a named companion object:

@JsExport interface Identity { class Metadata(val tag: String) companion object Registry { val defaultTag = "GUEST" } }

Currently, the @JsExport annotation is the only way to make your functions visible from Kotlin.

The @JsExport annotation is also available:

  • In common code in multiplatform projects. It only has an effect when compiling for the JavaScript target and allows you to also export Kotlin declarations that are not platform-specific.

  • Together with the @JsName annotation to specify the names for the generated and exported functions. This helps to resolve ambiguities in exports (like overloads for functions with the same name).

  • At the file level using @file:JsExport.

Support for value class export

You can export Kotlin's inline value classes as regular TypeScript classes.

To export a value class, mark it with the @JsExport annotation on the Kotlin side:

// Kotlin @JsExport @JvmInline value class Email(val address: String) { init { require(address.contains("@")) { "Invalid email" } } } @JsExport class AuthService { suspend fun login(email: Email): String = ... }

From the TypeScript side, it looks like a regular class:

// TypeScript import { AuthService, Email } from "..." const auth = new AuthService(); console.log(await auth.login(new Email("jane@example.com"))); // "Welcome, jane@example.com!" console.log(await auth.login(new Email("not-an-email"))); // "Invalid email"

@JsNoRuntime annotation

You can export Kotlin interfaces to JavaScript/TypeScript with the @JsNoRuntime annotation. It allows for direct mapping to regular TypeScript interfaces.

To export a Kotlin interface, for example from a Kotlin Multiplatform project:

  1. Annotate the Kotlin interface with @JsNoRuntime in common code:

    // commonMain import kotlin.js.JsNoRuntime @JsNoRuntime expect interface DataProcessor { fun process(data: String): Int }
  2. Provide the actual implementation with @JsNoRuntime in your JS-specific source code:

    // jsMain import kotlin.js.JsNoRuntime @JsNoRuntime actual interface DataProcessor { actual fun process(data: String): Int }
  3. On the TypeScript side, the interface will be mapped to a regular TypeScript interface:

    // Generated .d.ts export interface DataProcessor { process(data: string): number; }

For Kotlin Multiplatform projects, the general rules are:

  • Both expect and actual interface declarations must be annotated with @JsNoRuntime. The only exception is external implementations in platform-specific code on the actual side that require no annotation.

  • Using external interface declarations in common code on the expect side is prohibited. Instead, use regular interfaces annotated with @JsNoRuntime.

Exporting Kotlin interfaces with @JsNoRuntime has some restrictions. The annotation isn't allowed with:

  • external interfaces as they already behave as if they have @JsNoRuntime by default. Adding it results in a compiler warning.

  • is and as type checks.

  • Class references that use the ::class syntax.

  • Interfaces that are passed as reified type argument.

@JsStatic

The @JsStatic annotation instructs the compiler to generate additional static methods for the target declaration. This helps you use static members from your Kotlin code directly in JavaScript.

You can apply the @JsStatic annotation to functions defined in named objects, as well as in companion objects declared inside classes and interfaces. If you use this annotation, the compiler will generate both a static method of the object and an instance method in the object itself. For example:

// Kotlin class C { companion object { @JsStatic fun callStatic() {} fun callNonStatic() {} } }

Now, the callStatic() function is static in JavaScript while the callNonStatic() function is not:

// JavaScript C.callStatic(); // Works, accessing the static function C.callNonStatic(); // Error, not a static function in the generated JavaScript C.Companion.callStatic(); // Instance method remains C.Companion.callNonStatic(); // The only way it works

It's also possible to apply the @JsStatic annotation to a property of an object or a companion object, making its getter and setter methods static members in that object or the class containing the companion object.

This feature is Experimental. Share your feedback in our issue tracker, YouTrack.

Use BigInt type to represent Kotlin's Long type

Kotlin/JS uses JavaScript's built-in BigInt type to represent Kotlin Long values when compiling to modern JavaScript (ES2020).

To enable support for the BigInt type, you need to add the following compiler option to your build.gradle(.kts) file:

// build.gradle.kts kotlin { js { ... compilerOptions { freeCompilerArgs.add("-Xes-long-as-bigint") } } }

This feature is Experimental. Share your feedback in our issue tracker, YouTrack.

Use Long in exported declarations

Since Kotlin's Long type can compile to JavaScript's BigInt type, Kotlin/JS supports exporting Long values to JavaScript.

To enable this feature:

  1. Allow exporting Long in Kotlin/JS. Add the following compiler option to the freeCompilerArgs attribute in your build.gradle(.kts) file:

// build.gradle.kts kotlin { js { ... compilerOptions { freeCompilerArgs.add("-XXLanguage:+JsAllowLongInExportedDeclarations") } } }
  1. Enable the BigInt type. See how to enable it in Use BigInt type to represent Kotlin's Long type.

Use BigInt64Array type to represent Kotlin's LongArray type

Kotlin/JS can use JavaScript's built-in BigInt64Array type to represent Kotlin's LongArray values when compiling to JavaScript.

To enable support for the BigInt64Array type, add the following compiler option to your build.gradle(.kts) file:

// build.gradle.kts kotlin { js { ... compilerOptions { freeCompilerArgs.add("-Xes-long-as-bigint") } } }

This feature is Experimental. Share your feedback in our issue tracker, YouTrack.

Kotlin types in JavaScript

See how Kotlin types are mapped to JavaScript ones:

Kotlin

JavaScript

Comments

Byte, Short, Int, Float, Double

Number

Char

Number

The number represents the character's code.

Long

BigInt

Needs the -Xes-long-as-bigint compiler option configured.

Boolean

Boolean

String

String

Array

Array

ByteArray

Int8Array

ShortArray

Int16Array

IntArray

Int32Array

CharArray

UInt16Array

Carries the property $type$ == "CharArray".

FloatArray

Float32Array

DoubleArray

Float64Array

LongArray

BigInt64Array

BooleanArray

Int8Array

Carries the property $type$ == "BooleanArray".

List, MutableList

KtList, KtMutableList

Exposes an Array via KtList.asJsReadonlyArrayView or KtMutableList.asJsArrayView.

Map, MutableMap

KtMap, KtMutableMap

Exposes an ES2015 Map via KtMap.asJsReadonlyMapView or KtMutableMap.asJsMapView.

Set, MutableSet

KtSet, KtMutableSet

Exposes an ES2015 Set via KtSet.asJsReadonlySetView or KtMutableSet.asJsSetView.

Unit

Undefined

Exportable when used as return type, but not when used as parameter type.

Any

Object

Throwable

Error

enum class Type

Type

Enum entries are exposed as static class properties (Type.ENTRY).

Nullable Type?

Type | null | undefined

All other Kotlin types, except for those marked with @JsExport

Not supported

Includes Kotlin's unsigned integer types.

Additionally, it is important to know that:

  • Kotlin preserves overflow semantics for kotlin.Int, kotlin.Byte, kotlin.Short, kotlin.Char and kotlin.Long.

  • Kotlin cannot distinguish between numeric types at runtime (except for kotlin.Long), so the following code works:

    fun f() { val x: Int = 23 val y: Any = x println(y as Float) }
  • Kotlin preserves lazy object initialization in JavaScript.

29 May 2026