Newbie: using React components in Elm


#1

I want to write an app in Elm (specifically, a Progressive Web App). I want to be able to quickly prototype a UI using premade components from the Wide Web, maybe from npm ecosystem, maybe from React ecosystem. Actually, I’m not sure which, because I’m a JS noob (and I desperately want to stay one). The goal is to reuse, i.e. I don’t want to have to build my own html+css as much as possible. I’ve seen some React xml-like layouts and components and they seem perfect.

How can I do this in Elm? It can be non-idiomatic, I just want to do it as easily as possible to focus on the actual app, not some low level yak shaving.

I’ve seen a similar question on stackoverflow; but as an Elm and JS noob I don’t get much of it. I mostly see it as a few 2016 answers of “boooo, you shouldn’t, it’s not idiomatic!”, but some guy there also tried to hack something in 2017 and it seems to work for him? I’m interested in this constructive even if hacky and ugly attitude. Is this still the state of art as of 2018? Can somebody please help me and expand on this in an ELI5 way, that I could ideally repeat for any third party React component? I’d be super grateful!!!


#2

You might be looking for http://package.elm-lang.org/packages/rundis/elm-bootstrap/latest or http://package.elm-lang.org/packages/debois/elm-mdl/latest.


#3

Woohoo, that looks cool, thanks! Will certainly try them; should be enough to build some reasonable first prototype on top of them (esp. MDL, given I want to build a phone app).

The MDL package seems to have stalled; do you know if any of the numerous forks has picked the ball, by chance? For example, the dialog feature seems incomplete, while the bootstrap package seems to have a similar one in better shape. Or I don’t see support for a “drawer” widget, which would also be interesting to me.

Also, are there some more alternatives apart from your 2 suggestions, or is it all there is as of now?

And still somewhat interested in the original question, regardless!


#5

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:

  1. When the component is initialized on the Elm side, make a port call to JS giving it id of your component’s root element.
  2. On the JS side create the component and insert it into the DOM under your root element.
  3. Use Html.Keyed around your root element to prevent the virtual DOM algorithm from messing up its children (which it is not aware of).
  4. 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:

  1. Define a custom element like it is described in the article.
  2. 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.
  3. Use a disconnectedCallback if you need to do some cleanup when the component is removed from the DOM.
  4. 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).
  5. 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.
  6. 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.


#6

I have never used any of the packages, just seen them mentioned in news feeds. So unfortunately I don’t know the state of the forks. I don’t know of similiar packages either.


#7

I’m using MDL on a prototype project and it’s working well. You are right, from what I heard in the elm-mdl channel it’s stalled and there is a replacement, but don’t remember the name off the top of my head (on my phone atm, will look soon). If this is a prototype I’d say use it; it’s pretty complete and works well. If this will be maintained long-term, then I’d find a more maintained solution.


#8

Actually, MDL (the library created by Google) has also been replaced. I believe this is partially why development has stalled for elm-mdl.

Google’s replacement is called material-components-web (MCW?) and the new Elm package is called elm-mdc


#9

Thank you all very much for your help! I’m going with elm-mdl for now, plus some custom CSS hacking, trying to force it to my will on a case by case basis. I’m scared that CSS will punish me at some point, but for now I’m very satisfied. And I must say I’m genuinely astonished how much easier it is to build the UIs that way than in native Android Java. I feel like it should be opposite, but it’s not. Thank you all again for helping me and showing the way!

I’m also very interested in the Web Components approach; I feel I will hit a wall at some point with pure elm-mdl plus my amateur CSS, and WC sound like interesting shrink-wrapped composable & well isolated utilities. But I don’t know yet how to meld them into the awesome create-elm-app infrastructure. Which, by the way, works so flawlessly, quickly and fluently for me, that I feel really privileged and amazed, and grateful to the author (Eduard Kyvenko a.k.a. halfzebra) and all contributors that they built it, shared it and continued to improve it. I may be repeating but - thanks, you’re awesome!!!


#10

To use web components in Elm you may find some hints in this thread: Ways of embedding other libs or frameworks into Elm app?


#11

Yes, thanks! Actually I’ve already stumbled upon this thread a day or two ago. As of now, I believe I more or less understand how communication with Web Components can be done on Elm side (I think the article on elmprogramming.com is a nice summary; though it doesn’t mention the “Html.keyed” thing, which I still don’t grasp, but see mentioned quite often).

One thing I’m still unsure about, is how to properly integrate/“import” a Web Component into a project based on the awesome create-elm-app template. But I haven’t really tried yet; I’m still managing with some ad hoc HTML + CSS, though I feel like fighting with it at times.


#12

@akavel If a Web Component you wish to use is available on npm: https://github.com/halfzebra/create-elm-app/blob/master/template/README.md#installing-javascript-packages


#13

@akavel, do not use elm-mdl. The Material Design Light framework itself has been obsoleted. Use elm-mdc instead, which uses the Google supported version Material Design Web Components.


#14

Why change if MDL works for me? What important advantages over it does MDC have? I don’t understand the linked article with the “comparison”.