# Creating a custom element / webcomponent

**URL:** <https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988>\
**Category:** JavaScript\
**Created:** [August 1, 2017, 12:17pm UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988 "2017-08-01T12:17:45Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![dazraf](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/dazraf/32/8985_2.png) [@dazraf](https://discuss.kotlinlang.org/u/dazraf)\
**Post date:** [August 1, 2017, 12:17pm UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/1 "2017-08-01T12:17:45Z")

</div>

Hi,

Has anyone figured out how to create a custom element?

An example of this is documented here:

[Google developer docs](https://developers.google.com/web/fundamentals/getting-started/primers/customelements)

and further documented here:

[Mozilla docs](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Custom_Elements)

The principle is to call `window.customElements.define(elementName, <class literal>)`

In KotlinJS’ dom bindings, `customElements.define` is declared as

`fun define(name: String, constructor: () -> dynamic, options: ElementDefinitionOptions = definedExternally): Unit`

The following causes a compilation error:

```auto
    abstract class MyComponent : Element() {
      companion object {
        fun init() {
          console.log("registering")
          window.customElements.define("my-component", ::MyComponent )
          console.log("registered")
        }
      }
      init {
        this.textContent = "my-component"
      }
    }

```

Compilation fails at the line with `::MyComponent` because MyComponent is abstract. It has to be abstract because it cannot extend from `Element` (and `external` abstract class!) without implementing all members.

Any thoughts on how this problem could be resolved please?

thanks  
Fuzz.

---

<div class="post-metadata">

**Author:** ![dazraf](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/dazraf/32/8985_2.png) [@dazraf](https://discuss.kotlinlang.org/u/dazraf)\
**Post date:** [August 1, 2017, 12:21pm UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/2 "2017-08-01T12:21:05Z")

</div>

Just as a thought - is the use of `abstract` redundant and perhaps not helpful, when declaring with `external`?

---

<div class="post-metadata">

**Author:** ![Alexey.Andreev](https://avatars.discourse-cdn.com/v4/letter/a/e36b37/32.png) [@Alexey.Andreev](https://discuss.kotlinlang.org/u/Alexey.Andreev)\
**Post date:** [August 2, 2017, 7:53am UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/3 "2017-08-02T07:53:19Z")

</div>

> ```auto
> window.customElements.define("my-component", ::MyComponent )
> 
> ```

The proper usage would be something like

```auto
window.customElements.define(
    "my-component",
    MyComponent::class.js.unsafeCast<() -> dynamic>()
)

```

For convenience, you can declare a function like this:

```auto
fun CustomElementRegistry.define(
    name: String, 
    constructor: KClass<in Element>
) {
    define(name, constructor.js.unsafeCast<() -> dynamic>())
}

```

---

<div class="post-metadata">

**Author:** ![dazraf](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/dazraf/32/8985_2.png) [@dazraf](https://discuss.kotlinlang.org/u/dazraf)\
**Post date:** [August 2, 2017, 8:09am UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/4 "2017-08-02T08:09:21Z")

</div>

Thanks Alexey, will try that and report back.

Being able to create first class web components as Kotlin classes is very compelling 🙂

---

<div class="post-metadata">

**Author:** ![jdemeulenaere](https://avatars.discourse-cdn.com/v4/letter/j/ecc23a/32.png) [@jdemeulenaere](https://discuss.kotlinlang.org/u/jdemeulenaere)\
**Post date:** [September 16, 2017, 8:54pm UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/5 "2017-09-16T20:54:13Z")

</div>

Did you manage to create custom elements with KotlinJS ? I tried the code given by Alexey but unfortunately the generated code throws an error :

```
Uncaught TypeError: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function.
at new CounterElement (komponent_main.js:161)

```

Here is my code :

```
abstract class CustomElement : HTMLElement() {
	init {
		println("Creating custom element")
		textContent = "Hello world"
	}
}

fun <T : Element> defineElement(name: String, constructor: KClass<T>) {
	window.customElements.define(name, constructor.js.unsafeCast<() -> dynamic>())
}

fun main(args: Array<String>) {
	defineElement("custom-element", CustomElement::class)
}

```

---

<div class="post-metadata">

**Author:** ![Alexey.Andreev](https://avatars.discourse-cdn.com/v4/letter/a/e36b37/32.png) [@Alexey.Andreev](https://discuss.kotlinlang.org/u/Alexey.Andreev)\
**Post date:** [September 18, 2017, 8:27am UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/6 "2017-09-18T08:27:23Z")

</div>

I tried to create a custom element with hand-written ES5 code and it does not work:

```javascript
function CustomElement() {
    HTMLElement.call(this);
    console.log("Custom element created")
}
CustomElement.prototype = Object.create(HTMLElement.prototype);
CustomElement.prototype.constructor = HTMLElement;

window.customElements.define("custom-element", CustomElement);

```

However, with ES6 everything works as expected:

```auto
class CustomElement extends HTMLElement {
    constructor() {
        super();
        console.log("Custom element created");
    }
}

window.customElements.define("custom-element", CustomElement);

```

It seems that web components don’t support ES6 classes, at least according to [this discussion](https://github.com/w3c/webcomponents/issues/587). And Kotlin/JS does not support ES6 target. We plan to support ES6 eventually, but currently we don’t have any current plans for that.

---

<div class="post-metadata">

**Author:** ![jdemeulenaere](https://avatars.discourse-cdn.com/v4/letter/j/ecc23a/32.png) [@jdemeulenaere](https://discuss.kotlinlang.org/u/jdemeulenaere)\
**Post date:** [September 18, 2017, 8:43am UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/7 "2017-09-18T08:43:14Z")

</div>

I actually managed to make it work 🙂 This is indeed because KotlinJS compiles to ES5, so we have to add an additional polyfill custom-elements-es5-adapterjs as described here : [GitHub - webcomponents/webcomponentsjs: A suite of polyfills supporting the HTML Web Components specs](https://github.com/webcomponents/webcomponentsjs#user-content-custom-elements-es5-adapterjs)

I started to write a library to write custom elements with KotlinJS (as well as KotlinJS wrappers for Polymer elements) and I think that it looks quite promising so I can keep you up-to-date on that if you’d like 🙂

---

<div class="post-metadata">

**Author:** ![Thomas.Kratz](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/thomas.kratz/32/5345_2.png) [@Thomas.Kratz](https://discuss.kotlinlang.org/u/Thomas.Kratz)\
**Post date:** [November 25, 2017, 9:51am UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/8 "2017-11-25T09:51:55Z")

</div>

Has anyone of you a working example online that would help me getting started?

---

<div class="post-metadata">

**Author:** ![jdemeulenaere](https://avatars.discourse-cdn.com/v4/letter/j/ecc23a/32.png) [@jdemeulenaere](https://discuss.kotlinlang.org/u/jdemeulenaere)\
**Post date:** [January 22, 2018, 4:13pm UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/9 "2018-01-22T16:13:49Z")

</div>

I have a small working demo of a small POC I made a few months ago if you are still interested :

```
$ git clone https://github.com/jdemeulenaere/komponent.git
$ ./gradlew gulp_default
$ ./gradlew build -t

```

Fire up a local server and open index.html 🙂

---

<div class="post-metadata">

**Author:** ![kldmohammed](https://avatars.discourse-cdn.com/v4/letter/k/9de053/32.png) [@kldmohammed](https://discuss.kotlinlang.org/u/kldmohammed)\
**Post date:** [April 28, 2018, 4:49am UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/10 "2018-04-28T04:49:51Z")

</div>

Hi i want this polymer lib

---

<div class="post-metadata">

**Author:** ![Thomas.Kratz](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/thomas.kratz/32/5345_2.png) [@Thomas.Kratz](https://discuss.kotlinlang.org/u/Thomas.Kratz)\
**Post date:** [April 28, 2018, 5:46am UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/11 "2018-04-28T05:46:12Z")

</div>

Thanks for getting back tp me. Took a while for me to notice. I’ll give it a try today!

---

<div class="post-metadata">

**Author:** ![santoszv](https://avatars.discourse-cdn.com/v4/letter/s/f14d63/32.png) [@santoszv](https://discuss.kotlinlang.org/u/santoszv)\
**Post date:** [August 30, 2018, 12:34pm UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/12 "2018-08-30T12:34:39Z")

</div>

I made this for fun 🙂. Maybe somebody is still interested in this thread, so i posted it.

Kotlin code:

```
import org.w3c.dom.HTMLElement
import org.w3c.dom.OPEN
import org.w3c.dom.ShadowRootInit
import org.w3c.dom.ShadowRootMode
import kotlin.browser.window

fun main(args: Array<String>) {
    val xTestSpec = customElementSpec().apply {
        observedAttributes = arrayOf("prop")
        init = XTest::init.unsafeCast<(HTMLElement) -> Unit>()
        attributeChangedCallback = XTest::attributeChangedCallback.unsafeCast<(HTMLElement, String, String, String) -> Unit>()
    }
    val xTestConstructor = getCustomElementConstructor(xTestSpec)
    window.customElements.define("x-test", xTestConstructor)
}

abstract external class XTest : HTMLElement

fun XTest.init() {
    val shadow = attachShadow(ShadowRootInit(ShadowRootMode.OPEN)).unsafeCast<HTMLElement>()
    shadow.innerHTML = "<p>Works !!!</p>"
}

fun XTest.attributeChangedCallback(attrName: String, oldVal: String, newVal: String) {
    println("$attrName: $oldVal -> $newVal")
}

private fun customElementSpec(): CustomElementSpec = js("{}").unsafeCast<CustomElementSpec>()

private external interface CustomElementSpec {
    var observedAttributes: Array<String>?
    var init: ((receiver: HTMLElement) -> Unit)?
    var connectedCallback: ((receiver: HTMLElement) -> Unit)?
    var disconnectedCallback: ((receiver: HTMLElement) -> Unit)?
    var attributeChangedCallback: ((receiver: HTMLElement, attrName: String, oldVal: String, newVal: String) -> Unit)?
    var adoptedCallback: ((receiver: HTMLElement) -> Unit)?
}

private val getCustomElementConstructor: (spec: CustomElementSpec) -> () -> dynamic by lazy {
    function<(spec: CustomElementSpec) -> () -> dynamic>("spec", block = BLOCK)
}

@JsName("Function")
private external fun <T> function(vararg params: String, block: String): T

private const val BLOCK = """const myObservedAttributes = spec.observedAttributes;
const myInit = spec.init;
const myConnectedCallback = spec.connectedCallback;
const myDisconnectedCallback = spec.disconnectedCallback;
const myAttributeChangedCallback = spec.attributeChangedCallback;
const myAdoptedCallback = spec.adoptedCallback;

return class extends HTMLElement {

    static get observedAttributes() {
        if (myObservedAttributes) {
            return myObservedAttributes;
        }
    }

    constructor() {
        super();
        if (myInit) {
            myInit(this);
        }
    }

    connectedCallback() {
        if (myConnectedCallback) {
            myConnectedCallback(this);
        }
    }

    disconnectedCallback() {
        if (myDisconnectedCallback) {
            myDisconnectedCallback(this);
        }
    }

    attributeChangedCallback(attrName, oldVal, newVal) {
        if (myAttributeChangedCallback) {
            myAttributeChangedCallback(this, attrName, oldVal, newVal);
        }
    }

    adoptedCallback() {
        if (myAdoptedCallback) {
            myAdoptedCallback(this);
        }
    }
};"""

```

HTML code:

```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Test - Web Components</title>
    <script src="out/production/kotlin-webcomponents/lib/kotlin.js"></script>
    <script src="out/production/kotlin-webcomponents/kotlin-webcomponents.js"></script>
</head>
<body>

<x-test prop="test"></x-test>

</body>
</html>

```

Google Chrome 68.0.3440.106 (Build oficial) (64 bits) screenshot:

 ![google_chrome](https://us1.discourse-cdn.com/flex019/uploads/kotlinlang/original/2X/f/f4c3813be695f4d767f3a492efd8f195b1c89826.png)

Firefox Developer Edition 63.0b1 (64-bits) screenshot:

 ![firefox_developer_edition](https://us1.discourse-cdn.com/flex019/uploads/kotlinlang/original/2X/3/34bddafa2cbc6e91cbdde8a54d92828e6af2eb44.png)

---

<div class="post-metadata">

**Author:** ![Frauna](https://avatars.discourse-cdn.com/v4/letter/f/779978/32.png) [@Frauna](https://discuss.kotlinlang.org/u/Frauna)\
**Post date:** [April 30, 2020, 9:58am UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/13 "2020-04-30T09:58:31Z")

</div>

Hi ! is there now a way to create a custom element ?  
The solution proposed by @jdemeulenaere is not accessible anymore.

Thanks !

---

<div class="post-metadata">

**Author:** ![Martin\_Vysny](https://avatars.discourse-cdn.com/v4/letter/m/e56c9b/32.png) [@Martin\_Vysny](https://discuss.kotlinlang.org/u/Martin_Vysny)\
**Post date:** [June 9, 2021, 7:39am UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/14 "2021-06-09T07:39:35Z")

</div>

Unfortunately there doesn’t seem to be a way: Kotlin-js doesn’t currently compile to ES6. You can subscribe/vote for [https://youtrack.jetbrains.com/issue/KT-8373](https://youtrack.jetbrains.com/issue/KT-8373) and other related issues, to vote for the Kotlin guys to implement this.

---

<div class="post-metadata">

**Author:** ![mvanassche](https://avatars.discourse-cdn.com/v4/letter/m/a4c791/32.png) [@mvanassche](https://discuss.kotlinlang.org/u/mvanassche)\
**Post date:** [August 2, 2022, 12:05pm UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/15 "2022-08-02T12:05:34Z")

</div>

In the meantime, I have created a small project to allow creating web components in Kotlin JS  
[https://github.com/mvanassche/kotlin-web-component](https://kotlin-web-component)

---

<div class="post-metadata">

**Author:** ![mvanassche](https://avatars.discourse-cdn.com/v4/letter/m/a4c791/32.png) [@mvanassche](https://discuss.kotlinlang.org/u/mvanassche)\
**Post date:** [July 18, 2023, 8:27am UTC](https://discuss.kotlinlang.org/t/creating-a-custom-element-webcomponent/3988/16 "2023-07-18T08:27:43Z")

</div>

In Kotlin 1.9.0 ES6 is now experimental.  
It is now possible to do web components in Kotlin! For example:

```kotlin
external interface WebComponent {
    fun connectedCallback()
    fun disconnectedCallback()
    fun adoptedCallback()
    fun attributeChangedCallback(name: String, oldValue: String, newValue: String)
}

@JsExport
abstract class CustomElement : HTMLElement(), WebComponent {
    init {
        println("Creating custom element")
        val shadow = this.attachShadow(ShadowRootInit(ShadowRootMode.OPEN)).unsafeCast<HTMLElement>()
        shadow.innerHTML = "<p>Hello</p>"
    }
    override fun connectedCallback() {
        println("connectedCallback")
    }
}

```

```auto
kotlin {
    js(IR) {
        useEsModules()
   [...]
   }
}

tasks.withType<KotlinJsCompile>().configureEach {
    kotlinOptions {
        useEsClasses = true
    }
}

```
