elm / browser

Create Elm programs that run in browsers!
https://package.elm-lang.org/packages/elm/browser/latest/
BSD 3-Clause "New" or "Revised" License
313 stars 64 forks source link

Broken links in `Browser.application` with prerendered content #105

Open ChristophP opened 4 years ago

ChristophP commented 4 years ago

Problem For SEO reasons as well as to prevent FOUC we prerender our Elm apps with certain tools. That means that by the time the Elm app initializes the DOM is already filled and the Elm app is supposed to "take it from there". However, links are broken then, in the sense that they will cause a full page reload instead of be intercepted by Browser.application.

Cause

Before Browser.application initializes it seems to call _VirtualDom_virtualize() here in order to hydrate the vdom. Since the content is the same it would render initally the DOM will be left as is. This is problematic because links will not get the same treatment as other links in Browser.application that is happening here

Proposed solution Add special treatment for links when virtualizing the DOM in this scenario so that clicks on those links will be intercepted as well.

Possible workaround Whiping the DOM clean before Elm initializes. :-/

SSCCE

<html>
<!-- note the lack of whitespace after the body tag, to make sure no text nodes get inserted by the virtual DOM virtualize function -->
<body><a href="/blog">Prerendered: This link WILL NOT be intercepted by Browser.application</a>
  <script src="/elm.js"></script>
  <script>
    Elm.Main.init()
  </script>
</body>
</html>
module Main exposing (..)

import Browser
import Html exposing (..)
import Html.Attributes exposing (href)

view model =
    Browser.Document "SSCCE"
        [ a [ href "/blog" ] [ text "Prerendered: This link WILL NOT be intercepted by Browser.application" ]
        , br [] []
        , br [] []
        , a [ href "/blog" ] [ text "Dynamically rendered: This link WILL be intercepted by Browser application" ]
        ]

main =
    Browser.application
        { init = \() url key -> ( (), Cmd.none )
        , view = view
        , update = \msg model -> ( model, Cmd.none )
        , subscriptions = \() -> Sub.none
        , onUrlRequest = \urlRequest -> ()
        , onUrlChange = \url -> ()
        }
{
    "type": "application",
    "source-directories": [
        "src"
    ],
    "elm-version": "0.19.1",
    "dependencies": {
        "direct": {
            "elm/browser": "1.0.2",
            "elm/core": "1.0.5",
            "elm/html": "1.0.0"
        },
        "indirect": {
            "elm/json": "1.1.3",
            "elm/time": "1.0.0",
            "elm/url": "1.0.0",
            "elm/virtual-dom": "1.0.2"
        }
    },
    "test-dependencies": {
        "direct": {},
        "indirect": {}
    }
}
aggressivepixels commented 4 years ago

Just got hit by this, and can confirm that wiping the DOM is a valid workaround, but a fix would definitely be nice.