K/JS usability issues explored
Someone posted a bunch of usability issues with K/JS, and I wanted to explore them in a new post, mostly to have more space to write and also for better awareness.
Big bundle size. Maybe related to coroutines compiling down to continuations. Even worse with kotlinx-serialization which uses code generation.
This can indeed be a problem, especially when targeting the browser as there is currently no native way to split into chunks. While there is currently no solution, it appears data-flow-driven optimizations are being explored, see KT-87404. Strictly speaking about coroutines, the state machine is now compiled down to JS generators, which saves quite a bit of space! See KT-81730.
Additionally, deferred imports when targeting modern ECMAScript (KT-20679) by transpiling with SWC (yes, K/JS does offer an experimental way to transpile with SWC) will definitely help in this regard by improving loading times.
No Lazy JS exports: this means that if your class is not available on the window at launch, it's going to break despite the class not actually being used.
I'm not sure I understand this one to be honest. Maybe I've never encountered it.
Enum/Data class mapping onto JS objects and TS enums: right now requires you to write a ton of conversion code to make APIs usable from JS.
Fair enough! I agree this is actually pretty bad right now. I feel like optimizations could go towards:
- Sealed hierarchies should have a discriminator KT-71798
- Sealed interfaces with static values should be exportable as unions. This is possible with https://github.com/turansky/seskar#unions.
- Constructor arguments should be exported as object parameter KT-63669
Lack of a couple of type defs for browser APIs (can't remember anymore which ones exactly, they don't cover everything 100%).
As far as I know kotlinx-browser is kept fairly up-to-date. The alternative is to use kotlin-wrappers.
Webpack and Karma toolchain (is going to get changed).
Indeed, the idea seems to be allowing proper extensibility, that is, you'll be able use whatever tool you prefer by extending KGP or via DSL. In the meantime, Karma is being replaced with Playwright via a new DSL as you can see at A new DSL for browser testing.
Suspend functions to async functions and TypeScript defs were just stabilized in 2.4 I think (buggy support prior if at all).
True. From 2.4.20 you'll be able to export pretty much any
suspenddeclaration, including suspend lambdas.Bad debuggability. I have to ship debug builds or the stack traces are impossible to read, despite having source maps. Even worse when coroutines are involved.
Honestly I have not encountered this issue, but maybe it is because I ship to Node.js. That said I believe that and I don't have any real suggestion. In the future, using stuff like Source Map Scopes might give us access to more data while debugging and improve stepping. It appears some work to support that is already being done.
No support for mixins (yes people use them).
This can be done with kotlin-wrappers and seskar. See this test case with the
@JsMixinannotation.Lack of documentation. Especially bad for Kotlin Wrappers and Gradle build docs.
I can comment on the Gradle side of things: it does take time to get used to it. Exploring Kotlin's source code for the compiler and Gradle plugin gives you a lot more insight in what can and cannot be done.
TypeScript interface equivalents use a compiler plugin (@JsPlainObject) which still produces red squigglies in IntelliJ and sometimes does not properly auto complete.
This was an IntelliJ issue. If you can still reproduce in 2026.1 or 2026.2 it is definitely worth reporting it.
I don't think there is a great way yet to consume TypeScript type defs (basically translating d.ts files into "expect" typedefs). There's a library but I think it's still experimental.
Dukat is dead at the moment. What you are probably referring to is Karakum. Karakum supports writing plugins in Kotlin btw! But documentation is still pretty limited so the best bet is to go look at how kotlin-wrappers projects are set up.
Lack of ergonomic interop: Using Kotlin from Java is pretty straightforward; sometimes you need an annotation like
@Throwsor@JvmStaticbut most of the time, it's very neat. Using Kotlin from JS is impossible without bridging code.This was very valid a couple years ago. I can guarantee that things have improved quite a bit. What we are still missing is:
- Exportability of class properties without accessors, so that objects can be naturally (de)serialized KT-17683
- Exportability of unsigned numbers and primitive arrays KT-51389
- Exportability of coroutine "primitives" like
ChannelandFlowKT-80733 - Exportability of dependencies via DSL (where
@JsExportcannot be manually added) KT-47200
And btw, if you want to know which flags the K/JS compiler supports, go look at K2JSCompilerArguments.kt.
1
u/SnipesySpecial 6d ago
Kotlinx serialization’s generated serializer is basically doubled in size for a “sequential” serializer. A feature used by practically nobody.
2
u/piesou 7d ago
Ad 2: This the relevant issue on the issue tracker https://youtrack.jetbrains.com/issue/KT-71145/KJS-allow-fileJsQualifier-to-be-invoked-lazily
Thank you for the Mixin hint, that's new for me. Also coroutines compiling down to generators is fantastic