How to create custom elements (Web Components) with ClojureScript?

Custom elements rely on ES6 classes to be defined, a simple example in JS:

class MyElement extends HTMLElement {}
customElements.define("my-element", MyElement)

See Using custom elements on MDN for a detailed explanation

Since ES6 classes cannot reliably be transpiled, and ClojureScript (as far as I know) doesn’t emit ES6 class syntax, the only way I can think of to generate a custom element is with js*, e.g.:

(def my-element (js* "class MyElement extends HTMLElement {}"))
(js/customElements.define "my-element" my-element)

Is there a better way? Or any plans to add support for ES6 classes in ClojureScript?

I played with web components a long while ago and I got it working fine, just needs a bit a macro sugar to make it look nicer. It might work differently nowadays since a lot may have changed in the 3 years since writing that.

Are you sure ClojureScript/Closure can’t emit ES6?

I haven’t tested it but the :language-in and :language-out options seem like they would work with ES6.

The Closure Compiler wiki definitely says it can handle ES6.

Closure handles ES6 just fine but ClojureScript has no built-in support to emit ES6 code (eg. class).

Thanks for the example, it is still working, the web component is created and attached to the DOM. :slight_smile:

But I’m still trying to figure out how to mock some ES6 features. In many web components examples, apart of extending HTMLElement, the constructor is overridden (and super() is called) . So far I didn’t find a way of doing that with ClojureScript. Do you have more ClojureScript - Web Components examples?

For context, what I’m really trying to do is to create a ClojureScript wrapper around LitElement

1 Like
(defn component []
  (js/Reflect.construct js/HTMLElement #js [] component))

component is the constructor function and the js/Reflect call is basically the super() call. So this is basically class component extends HTMLElement.

4 Likes

@theller’s reply helped me out a bunch on this topic. For posterity, here’s my simple “render reagent/re-frame component as a webcomponent’s shadow DOM” function built out

3 Likes

I just found that there is a ticket about adding support for ES6 classes to ClojureScript:
https://clojure.atlassian.net/browse/CLJS-3084

This is really cool @Mechrophile! Works well!