Configuring Browser.element for zero‑downtime incremental Elm migration alongside legacy JavaScript
0 reputation · 26 May 2021, 07:21 UTC
0 reputation · 26 May 2021, 07:21 UTC
Goal: embed an Elm application into an existing server‑rendered page using Browser.element, serve the compiled elm.js as a static asset, and load it conditionally via a feature flag so the legacy JavaScript continues to run unchanged, enabling a zero‑downtime migration of a small frontend feature.
Constraints/uncertainty: Browser.element offers no CSS or DOM encapsulation, meaning global stylesheets or third‑party scripts can leak into or alter the Elm‑rendered subtree; port communication lacks compile‑time enforcement on the JavaScript side, which can result in exceptions or silent message drops if the Elm and JS handlers diverge; achieving true zero‑downtime relies on deployment mechanics such as conditional script loading or feature flags, which Elm does not provide natively.
Questions: What approaches can isolate Elm‑generated CSS from global stylesheets when using Browser.element? How can teams enforce reliable, type‑safe port contracts across the Elm‑JS boundary without runtime errors? Is there a recommended pattern for coordinating feature‑flagged script tags with Browser.element mounts to avoid duplicate initializations or state loss?
34775 reputation · 26 May 2021, 16:10 UTC
To embed an Elm Browser.element app into an existing server-rendered page while keeping legacy JavaScript running, the following three concerns must be addressed:
Below are concrete, step‑by‑step recommendations for each area, followed by a minimal bootstrap snippet that ties them together.
<div id="elm-root" class="elm-namespace"></div>.
elm-css or elm-styled, configure the namespace option so that generated classes become elm-namespace__button, elm-namespace__card, etc.<custom-elm> element that attaches a Shadow DOM. The Shadow DOM automatically scopes styles, but you must ensure no external scripts query the subtree.elm.js, inspect the --stats output to confirm that all class names contain the chosen namespace or are otherwise scoped.ajv on the JavaScript side to reject malformed messages before they reach Elm.
sendMsg : (String, Int) -> msg, the JS validator should enforce that the payload is an array with a string and a number.elm-test to test the Elm side’s handling of the same payloads. Any runtime error in Elm caused by a port mismatch will surface during this test stage.elm.js as a static asset with a cache‑busting hash (e.g. elm.abc123.js). Use the defer attribute so the script does not block page rendering.elm-loader.js) that performs the following:
window.FEATURE_ELM, which is set by the server or a feature-flag service.elm.js via import() or a <script> tag.elm.js resolves, call Elm.Main.init({ node: document.getElementById('elm-root'), flags: /*…*/ }).window.__ELM_APP__ = true, to prevent duplicate mounts if the page re‑renders.elm-root container only when the feature flag is enabled. If the flag is off, render the legacy UI as usual.To fine‑tune the bootstrap logic, could you let us know whether the feature flag is delivered as a static JavaScript variable, a cookie, or via a remote feature‑flag service? This will affect whether you need to poll for the flag or can rely on a single read during page load.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.