# Drop-in replacement for \`Browser.document\` with element mount point and title – no ports needed

**URL:** <https://discourse.elm-lang.org/t/drop-in-replacement-for-browser-document-with-element-mount-point-and-title-no-ports-needed/8527>\
**Category:** Show and Tell\
**Created:** [July 23, 2022, 2:28am UTC](https://discourse.elm-lang.org/t/drop-in-replacement-for-browser-document-with-element-mount-point-and-title-no-ports-needed/8527 "2022-07-23T02:28:55Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![GabrielBoehme13](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/gabrielboehme13/32/4635_2.png) [@GabrielBoehme13](https://discourse.elm-lang.org/u/GabrielBoehme13)\
**Post date:** [July 23, 2022, 2:28am UTC](https://discourse.elm-lang.org/t/drop-in-replacement-for-browser-document-with-element-mount-point-and-title-no-ports-needed/8527/1 "2022-07-23T02:28:55Z")

</div>

It’s [known](https://discourse.elm-lang.org/t/fullscreen-elm-app-in-0-19-childnode-issue-reopened/3174/25) that `Browser.application` and `Browser.document` can encounter runtime errors in the wild, due to their assumption that Elm has complete control over the DOM. This assumption doesn’t play nicely with browser extensions.

[This recent post](https://discourse.elm-lang.org/t/published-an-npm-package-to-prevent-browser-extensions-from-crashing-elm-apps/8524) by @Ethan_Martin, providing a Webpack-based fix for both `Browser.application` and `Browser.document` (thank you!), prompted me to share something I discovered recently: a way to create a drop-in replacement for `Browser.document` within Elm, without requiring the use of ports to set the document title.

The key to making this work has to do with the lenient way browsers look for the document title. In all the browsers I tested (Firefox, Safari, IE, Chromium-based browsers), they use the first `<title>` element they encounter anywhere in the DOM – even if it’s in the `<body>`!

This means we can use `Browser.element` with `Html.node` to provide the `<title>` element from Elm. The only restriction is that Elm’s `<title>` _must_ be the first `<title>` element in the DOM for it to take effect – any earlier `<title>` element specified in the HTML, even an empty one, will override it.

Below is a demo app showing the simplest way I found to implement this in Elm. (Note that it won’t work via `elm reactor`, which specifies its own `<title>` element.)

```auto
module Demo exposing (main)

import Browser
import Html

documentElement :
  { init : flags -> ( model, Cmd msg )
  , subscriptions : model -> Sub msg
  , update : msg -> model -> ( model, Cmd msg )
  , view : model -> Browser.Document msg
  }
  -> Program flags model msg
documentElement { init, subscriptions, update, view } =
  Browser.element
    { init = init
    , subscriptions = subscriptions
    , update = update
    , view =
      \model ->
        let
          { title, body } =
            view model
        in
        Html.div [] <| Html.node "title" [] [Html.text title] :: body
    }

main : Program () () msg
main =
  documentElement
    { init = \_ -> ( (), Cmd.none )
    , subscriptions = \_ -> Sub.none
    , update = \_ _ -> ( (), Cmd.none )
    , view =
      \_ ->
        { title = "Hello World!"
        , body = [Html.text "It works!"]
        }
    }

```

Hope this is helpful!

---

<div class="post-metadata">

**Author:** ![GabrielBoehme13](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/gabrielboehme13/32/4635_2.png) [@GabrielBoehme13](https://discourse.elm-lang.org/u/GabrielBoehme13)\
**Post date:** [July 24, 2022, 2:31am UTC](https://discourse.elm-lang.org/t/drop-in-replacement-for-browser-document-with-element-mount-point-and-title-no-ports-needed/8527/2 "2022-07-24T02:31:43Z")

</div>

After thinking about this some more, here’s a more elegant implementation. Rather than creating a `documentElement` wrapper function around `Browser.element`, all that’s really needed is this transformation function:

```auto
documentToHtml : Browser.Document msg -> Html.Html msg
documentToHtml { title, body } =
  Html.div [] <| Html.node "title" [] [Html.text title] :: body

```

This allows you to transition your programs from using `Browser.document` like this…

```auto
main =
  Browser.document
    { init = init
    , subscriptions = subscriptions
    , update = update
    , view = view
    }

```

…to using `Browser.element` like this, without having to modify your existing `view` function:

```auto
main =
  Browser.element
    { init = init
    , subscriptions = subscriptions
    , update = update
    , view = \model -> view model |> documentToHtml
    }

```

Or alternatively:

```auto
main =
  Browser.element
    { init = init
    , subscriptions = subscriptions
    , update = update
    , view = view >> documentToHtml
    }

```

---

<div class="post-metadata">

**Author:** ![passiomatic](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/passiomatic/32/1250_2.png) [@passiomatic](https://discourse.elm-lang.org/u/passiomatic)\
**Post date:** [July 25, 2022, 7:34am UTC](https://discourse.elm-lang.org/t/drop-in-replacement-for-browser-document-with-element-mount-point-and-title-no-ports-needed/8527/3 "2022-07-25T07:34:23Z")

</div>

Regarding putting the `title` in the `body`, I would suggest to test this with software tools like SiteImprove ([Web Accessibility tools to help you achieve accessibility compliance](https://www.siteimprove.com/platform/accessibility/)) which check for accessibility compliance.

I can see that browsers may be lenient about `title` placement in the DOM but I’m suspicious about the HTML correctness of the final result—not to mention the SEO implications.

---

<div class="post-metadata">

**Author:** ![GabrielBoehme13](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/gabrielboehme13/32/4635_2.png) [@GabrielBoehme13](https://discourse.elm-lang.org/u/GabrielBoehme13)\
**Post date:** [July 25, 2022, 7:41pm UTC](https://discourse.elm-lang.org/t/drop-in-replacement-for-browser-document-with-element-mount-point-and-title-no-ports-needed/8527/4 "2022-07-25T19:41:51Z")

</div>

That’s a very good point @passiomatic – my solution here _is_ a hacky workaround. Even though browsers accept a `<title>` in within `<body>`, the [HTML standard](https://html.spec.whatwg.org/#the-title-element) is clear that it should always be specified within `<head>` – so any tooling expecting standards-compliant behavior won’t like this approach.

In my testing, I _did_ discover that you could start with a `<title>` in your static HTML, then remove it from the DOM via Javascript like this:

```nohighlight
document.head.removeChild(document.querySelector('title'))

```

…which then allowed Elm’s body title to take effect from that point onward. I don’t know if this is an improvement, or just compounding the issues inherent to this approach. 😬

In hindsight, a better solution might be to use a [custom element](https://guide.elm-lang.org/interop/custom_elements.html) that sets `document.title`. That would provide a more standards-compliant port-free way to to continue updating the document title from Elm when switching from `Browser.document` to `Browser.element`.

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [August 4, 2022, 7:41pm UTC](https://discourse.elm-lang.org/t/drop-in-replacement-for-browser-document-with-element-mount-point-and-title-no-ports-needed/8527/5 "2022-08-04T19:41:55Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
