How can you create Web Components using Kotlin/JS?...
# javascript
m
How can you create Web Components using Kotlin/JS? I did try to follow this guide and, while it did work in the past, it seems that the
kotlin-browser
bindings for Web Components have changed quite a bit, and now I'm not able to get a custom element to work
Here's the code:
Copy code
import web.components.ShadowRootInit
import web.components.ShadowRootMode
import web.components.customElements
import web.components.open
import web.dom.TagName
import web.html.HTMLElement

fun main() {
    println("Howdy!")
    customElements.define(TagName("test-element"), TestElement::class.js)
    println("Registered element!")
}

@JsExport
@JsName("TestElement")
class TestElement : HTMLElement, web.components.CustomElement {
    companion object {
        @JsStatic
        fun observedAttributes(): List<String> {
            return listOf()
        }
    }

    init {
        println("Initialized!")

        this.attachShadow(ShadowRootInit(mode = ShadowRootMode.open))

        this.shadowRoot!!.innerHTML = "test"
    }

    override val connectedCallback = {
        println("connected")
    }
}
It does not throw any error when registering the component, but none of the debug print messages of the custom element are logged (only the
Howdy!
and
Registered element!
are printed) nor the callback is called Kotlin 2.2.20 +
kotlin-browser:2025.10.4
a
And could you please also share the HTML file you use the
test-element
tag?
t
It's recommended to use
CustomElementReference
to avoid redundant
@JsExport
side effects (if you used
@JsExport
as workaround
Copy code
fun main() {
    println("Howdy!")
    customElements.define(TagName("test-element"), TestElement)
    println("Registered element!")
}

class TestElement : HTMLElement, web.components.CustomElement {
    init {
        println("Initialized!")

        this.attachShadow(ShadowRootInit(mode = ShadowRootMode.open))

        this.shadowRoot!!.innerHTML = "test"
    }

    override val connectedCallback = {
        println("connected")
    }

    companion object : CustomElementReference(TestElement::class.js) 
}
m
@Artem Kobzar
Copy code
<html>
    <head>
        <script src="WebComponentsTest.js"></script>
    </head>
    <body>
        <test-component></test-component>
    </body>
</html>
t
target = "es2015"
?
m
@turansky yup maybe I'm doing something stupid (I tried your code with some small changes because it didn't compile)
Copy code
package com.mrpowergamerbr.webcomponentstest

import web.components.CustomElementReference
import web.components.ShadowRootInit
import web.components.ShadowRootMode
import web.components.customElements
import web.components.open
import web.dom.TagName
import web.html.HTMLElement

fun main() {
    println("Howdy!")
    customElements.define(TagName("test-element"), TestElement::class.js)
    println("Registered element!")
}

class TestElement : HTMLElement, web.components.CustomElement {
    init {
        println("Initialized!")

        this.attachShadow(ShadowRootInit(mode = ShadowRootMode.open))

        this.shadowRoot!!.innerHTML = "test"
    }

    override val connectedCallback = {
        println("connected")
    }

    companion object : CustomElementReference<TestElement>(TestElement::class.js)
}
but it still didn't work ๐Ÿ˜ž, only the
Howdy!
and
Registered element!
shows up here's the build script
Copy code
import org.jetbrains.kotlin.gradle.dsl.JsModuleKind

plugins {
    kotlin("multiplatform") version "2.2.20"
}

repositories {
    mavenCentral()
}

kotlin {
    js {
        browser()
        binaries.executable()

        compilerOptions {
            // Enable ES6
            target = "es2015"
            useEsClasses = true
            this.moduleKind.set(JsModuleKind.MODULE_ES)
        }
    }

    sourceSets {
        jsMain {
            dependencies {
                implementation("org.jetbrains.kotlin-wrappers:kotlin-js:2025.10.4")
                implementation("org.jetbrains.kotlin-wrappers:kotlin-browser:2025.10.4")
            }
        }
    }
}
t
Catched!
Weird problem ๐Ÿ˜ž
There are 3 problems: 1. Without
@JsExport
we don't receive right constructor - bug 2. For some reason Kotlin/JS remove
connectedCallback
field - bug (see screenshot) 3. Kotlin/JS generate invalid constructor if no
()
call on parent class a.
: HTMLElement
- invalid constructor generated b.
: HTMLElement()
- valid constructor generated @MrPowerGamerBR could you please report them?
m
@turansky just to be sure: that would be a Kotlin/JS bug report right? (as in: report it against YouTrack, not kotlin wrappers) Also should I create a separate for each of the bugs?
(I guess it would be a Kotlin/JS bug because if it was a Kotlin Wrappers bug you would've already fixed it lol)
๐Ÿ˜œ 1
t
that would be a Kotlin/JS bug report right?
Yes
Also should I create a separate for each of the bugs?
It's better to do separate issues
I will add them in this meta issue
m
will do as soon as possible, I will send them here after opening them ๐Ÿ™‚
t
Thank you! ๐Ÿ™
kodee loving 1
a
Oh hey, that's my blog! I must say, it took me quite a long while to figure out how to get it working and I also ran into quite some bugs from Kotlin/JS. Thanks for figuring it out and I will update my blog when possible
t
@MrPowerGamerBR we can add temp workarounds (I have some ideas) for
connectedCallback
and other callbacks if you are interested ๐Ÿ˜‰
m
@turansky thankfully I don't need it because I wanted to use Web Components just for fun, so it is not something that I really need right now, but thanks for your help! kodee loving
๐Ÿ™„ 1