It’s surely possible to use JS components in your Elm app and there is nothing wrong with it, but you should do this rather in exceptional cases when reimplementing such components in Elm is impossible or very hard in practice. The most common examples are Google Maps and rich text editor.
You have two options: ports or web components.
With ports the sequence of actions is the following:
- When the component is initialized on the Elm side, make a port call to JS giving it id of your component’s root element.
- On the JS side create the component and insert it into the DOM under your root element.
- Use Html.Keyed around your root element to prevent the virtual DOM algorithm from messing up its children (which it is not aware of).
- Communicate with the JS component via ports.
The biggest downsides of this approach is that you don’t have a declarative interface to your component on the Elm side and you can’t implement your component as a pure function since it requires sending Cmds to use ports.
I recommend to use web components instead. Web components API consists of four parts: custom elements, shadow DOM, HTML imports and HTML templates. You only need custom elements. Basically this allows you to define custom HTML tags like <my-component> and perform some actions whenever they are inserted or removed from the DOM.
To get started see this talk and this article. The plan is the following:
- Define a custom element like it is described in the article.
- Define a
connectedCallback method inside of which initialize your component and add it to the DOM using for example document.createElement and then this.appendChild.
- Use a
disconnectedCallback if you need to do some cleanup when the component is removed from the DOM.
- In your Elm app use
Html.node "my-component" [] [] to render your component. Use Html.Attributes.attribute and Html.Attributes.property to pass data to it. Use attribute for simple things like strings and property for passing JSON. Then on the JS side use this.getAttribute, attributeChangedCallback and property setters to listen for changes in attributes/properties and react accordingly (see the article, it has some examples).
- Use
this.dispatchEvent(new CustomEvent("my-event", { detail: { some: data }}) to send events to Elm and Html.Events.on to listen for them. Read more about CustomEvent here.
- Keep as much state of your component as you can in your Elm app and pass it down to JS (make your Elm app the source of truth). You may still want to use
Html.Keyed if some critical state is kept in JS.
Web components don’t have perfect browser support so you’ll need a polyfill. You’ll also need a polyfill for CustomEvent for IE. You can use this package or just copy it directly from here.
PS: I don’t have much experience using web components myself so if somebody thinks that I said something wrong please correct me.