# Html.fragment : List (Html msg) -\> Html msg

**URL:** <https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992>\
**Category:** Request Feedback\
**Created:** [February 23, 2021, 11:35am UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992 "2021-02-23T11:35:50Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Janiczek](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/janiczek/32/4516_2.png) [@Janiczek](https://discourse.elm-lang.org/u/Janiczek)\
**Post date:** [February 23, 2021, 11:35am UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/1 "2021-02-23T11:35:50Z")

</div>

Hello folks! React has [“fragments”](https://reactjs.org/docs/fragments.html) with roughly the type annotation that I put into the title.

I feel like these would sometimes be useful when writing Elm views, in a similar vein to how `Html.Extra.viewIf` and `Html.Attributes.Extra.attributeIf` are useful.

## Motivating example

I have (after SSCCE-fication):

```elm
buttonView : String -> Msg -> Html Msg

buttonsView : Html Msg
buttonsView =
  Html.div []
    [ buttonView "Foo" DoFoo
    , buttonView "Bar" DoBar
    , buttonView "Save" Save
    ]

```

and I’ve just received an extra requirement from my project manager: if the entity is saved, show buttons `"Save as new"` and `"Save changes"` instead of the `"Save"` button.

So, _ideally_ I’d love to do

```elm
buttonsView : { isSaved : Bool } -> Html Msg
buttonsView {isSaved} =
  let
    saveButtons : Html Msg
    saveButtons =
        if isSaved then
            Html.fragment
                [ buttonView "Save as new" SaveAsNew
                , buttonView "Save changes" SaveChanges
                ]
        else
            buttonView "Save" Save
  in
  Html.div []
    [ buttonView "Foo" DoFoo
    , buttonView "Bar" DoBar
    , saveButtons
    ]

```

but since I don’t have `Html.fragment`, my (IMHO) best solution is to do:

```elm
buttonsView : { isSaved : Bool } -> Html Msg
buttonsView {isSaved} =
  Html.div []
    [ buttonView "Foo" DoFoo
    , buttonView "Bar" DoBar
    , Html.Extra.viewIf isSaved <| buttonView "Save as new" SaveAsNew
    , Html.Extra.viewIf isSaved <| buttonView "Save changes" SaveChanges
    , Html.Extra.viewIf (not isSaved) <| buttonView "Save" Save
    ]

```

(or put the `if` in the top level and duplicate the `Foo` and `Bar` cases in both branches of the `if`.)  
(or worse, meddle with `::` and `++` which personally I don’t like in my views because of how it messes up the otherwise regular formatting.)  
(or even worse, wrap things in another `Html.div`, which has, let’s say, CSS side-effects.)

That is to say, we have many workarounds, but it seems to me that our `view` code might be nicer if we had `Html.fragment : List (Html msg) -> Html msg`.

* * *

## Questions

- Do you think `Html.fragment` would be useful?
- Are there some associated risks?  
(I"m only aware of the need to also have a `Html.Keyed.fragment`.)

* * *

Aside: I get unnecessarily excited by the thought that `Html a` would become a Semigroup and a Monoid (we already have an identity element, but we don’t have a well-behaving concat function… `Html.div []` and friends don’t cut it 😄 )

---

<div class="post-metadata">

**Author:** ![rupert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rupert/32/1775_2.png) [@rupert](https://discourse.elm-lang.org/u/rupert)\
**Post date:** [February 23, 2021, 11:49am UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/2 "2021-02-23T11:49:54Z")

</div>

It would be nice. I suppose you could implement this by defining your own `Fragment` type? Does that mean you’d have to replicate the entire `Html` API though? and also it might be less efficient than what `Html.fragment` operation could achieve?

---

<div class="post-metadata">

**Author:** ![Janiczek](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/janiczek/32/4516_2.png) [@Janiczek](https://discourse.elm-lang.org/u/Janiczek)\
**Post date:** [February 23, 2021, 11:54am UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/3 "2021-02-23T11:54:03Z")

</div>

> [@rupert](#):
>
> and also it might be less efficient than what `Html.fragment` operation could achieve?

The best performance would probably be achieved if `fragment` was a primitive baked into `elm/virtual-dom` and handled by the reconciliator/patcher/… (I don’t know the nomeclature).

> [@rupert](#):
>
> I suppose you could implement this by defining your own `Fragment` type? Does that mean you’d have to replicate the entire `Html` API though?

I guess having this be a type separate to `Html` would be possible; but as you imply, the Elm code would probably do a lot of `List.concat` or something similar under the hood, yes.

(And for me personally, if the choices are “wait for it forever and work around it in the meantime” vs “implement my own Html-like module”, I’m probably sticking with the status quo 😅 )

---

<div class="post-metadata">

**Author:** ![rupert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rupert/32/1775_2.png) [@rupert](https://discourse.elm-lang.org/u/rupert)\
**Post date:** [February 23, 2021, 11:58am UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/4 "2021-02-23T11:58:40Z")

</div>

> [@Janiczek](#):
>
> (And for me personally, if the choices are “wait for it forever and work around it in the meantime” vs “implement my own Html-like module”, I’m probably sticking with the status quo 😅 )

How about PM Evan, see if he likes the idea and would potentially be open to a Pull Request if you wrote the feature?

I think this can be done whilst retaining full backward compatability on `elm/virtual-dom`, that is, it could be done as a minor release?

---

<div class="post-metadata">

**Author:** ![rupert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rupert/32/1775_2.png) [@rupert](https://discourse.elm-lang.org/u/rupert)\
**Post date:** [February 23, 2021, 12:01pm UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/5 "2021-02-23T12:01:31Z")

</div>

`Html.fragment` is sort of equivalent to `Cmd.batch` and `Sub.batch`. Would you also define `Html.none == Html.fragment []` in the API?

---

<div class="post-metadata">

**Author:** ![allanderek](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/allanderek/32/1360_2.png) [@allanderek](https://discourse.elm-lang.org/u/allanderek)\
**Post date:** [February 23, 2021, 12:14pm UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/6 "2021-02-23T12:14:46Z")

</div>

I would probably do the following (or whatever is similar but compiles I haven’t checked the parsing of `::`)

```elm
buttonsView : { isSaved : Bool } -> Html Msg
buttonsView {isSaved} =
  let
    saveButtons : Html Msg
    saveButtons =
        if isSaved then
                [ buttonView "Save as new" SaveAsNew
                , buttonView "Save changes" SaveChanges
                ]
        else
            [buttonView "Save" Save]
  in
  Html.div []
    ( buttonView "Foo" DoFoo
      :: buttonView "Bar" DoBar
      :: saveButtons
    )

```

Mostly because I think it is the most similar to what you are wishing for. Which is why I would also prefer `Html.fragment` in particular your SSCCE-ified version is actually pretty kind, because it has the _‘list’_ part last. This would become much more challenging if say you wanted the `buttonView "Bar" DoBar` button to be the last one.

As a further point in favour of `Html.fragment` it’s not just the pain of writing an example such as the one you’ve given, it’s the refactoring of an existing code that is painful. You’re forced to basically the entire enclosing element, whereas using `Html.fragment` you would only change that part that you wish to change.

I can’t think of any downsides off the top of my head, but, and I’m not sure why, I suspect there is a major gotcha that I’ve not thought of here.

---

<div class="post-metadata">

**Author:** ![Janiczek](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/janiczek/32/4516_2.png) [@Janiczek](https://discourse.elm-lang.org/u/Janiczek)\
**Post date:** [February 23, 2021, 12:15pm UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/7 "2021-02-23T12:15:13Z")

</div>

> [@rupert](#):
>
> Would you also define `Html.none == Html.fragment []` in the API?

We already kinda cheat `Html.none` (btw, `Html.Extra.nothing` is a thing) with `Html.text ""`. But having it sanctioned (and perhaps done using `Html.fragment []` instead) would be a nice polishing of the API, yes.

---

<div class="post-metadata">

**Author:** ![DullBananas](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dullbananas/32/3709_2.png) [@DullBananas](https://discourse.elm-lang.org/u/DullBananas)\
**Post date:** [February 23, 2021, 4:13pm UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/8 "2021-02-23T16:13:13Z")

</div>

This should also be done with attributes. This package already does it:

[https://package.elm-lang.org/packages/arowM/html/latest/](https://package.elm-lang.org/packages/arowM/html/latest/)

---

<div class="post-metadata">

**Author:** ![pdamoc](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/pdamoc/32/36_2.png) [@pdamoc](https://discourse.elm-lang.org/u/pdamoc)\
**Post date:** [February 24, 2021, 2:57pm UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/9 "2021-02-24T14:57:04Z")

</div>

When I encounter this type of situation, the solution usually looks like this:

```elm
buttonsView : { isSaved : Bool } -> Html Msg
buttonsView { isSaved } =
    [ [ buttonView "Foo" DoFoo
      , buttonView "Bar" DoBar
      ]
    , if isSaved then
        [ buttonView "Save as new" SaveAsNew
        , buttonView "Save changes" SaveChanges
        ]

      else
        [buttonView "Save" Save]
    ]
        |> List.concat
        |> div []

```

But yeah, having a way to say “Insert these elements as siblings in this list of children” would help as I ended up using this pattern frequent enough.

---

<div class="post-metadata">

**Author:** ![aklaing](https://avatars.discourse-cdn.com/v4/letter/a/6bbea6/32.png) [@aklaing](https://discourse.elm-lang.org/u/aklaing)\
**Post date:** [February 24, 2021, 10:30pm UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/10 "2021-02-24T22:30:09Z")

</div>

I have been using an approach similar to this, which works for all types of lists:

```elm
    when: Bool -> List a -> List a -> List a
    when condition subList accumulator =
        if condition
        then (subList ++ accumulator)
        else accumulator

    always: List a -> List a -> List a
    always subList accumulator =
           when True

```

Then we can write something similar to this:

```elm
    buttonsView : { isSaved : Bool } -> Html Msg
    buttonsView {isSaved} =
       Html.div []
         (always
             [ buttonView "Foo" DoFoo
             , buttonView "Bar" DoBar
             ] <|
          when isSaved
             [ buttonView "Save as new" SaveAsNew
             , buttonView "Save changes" SaveChanges
             ] <|
          when (not isSaved)
             [buttonView "Save" Save] [])

```

You could also define `never` as `when False`, and also define variants that take a single element instead of a subList, using `::` instead of `++`. Honestly, when I write UI code that is many pages long with maybe twenty conditions, this style looks pretty easy to read IMHO.

ps. How do you all get your code syntax-highlighted?

---

<div class="post-metadata">

**Author:** ![mattpiz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mattpiz/32/860_2.png) [@mattpiz](https://discourse.elm-lang.org/u/mattpiz)\
**Post date:** [February 25, 2021, 12:00am UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/11 "2021-02-25T00:00:46Z")

</div>

> [@aklaing](#):
>
> ps. How do you all get your code syntax-highlighted?

you add “elm” after the first triple quote indicating the start of a code block quote like this

````
```elm
your code
```

````

---

<div class="post-metadata">

**Author:** ![aklaing](https://avatars.discourse-cdn.com/v4/letter/a/6bbea6/32.png) [@aklaing](https://discourse.elm-lang.org/u/aklaing)\
**Post date:** [February 25, 2021, 1:25am UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/12 "2021-02-25T01:25:03Z")

</div>

Thanks!! I fixed it.

---

<div class="post-metadata">

**Author:** ![aklaing](https://avatars.discourse-cdn.com/v4/letter/a/6bbea6/32.png) [@aklaing](https://discourse.elm-lang.org/u/aklaing)\
**Post date:** [February 25, 2021, 2:27am UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/13 "2021-02-25T02:27:58Z")

</div>

> [@aklaing](#):
>
> You could also define variants that take a single element instead of a subList, using `::` instead of `++` .

Actually I take that back. For this style, `::` is not very useful because you would need ordinary parenthesis to do the work of keeping the single item together. So instead of writing this:

```elm
    ...
    whenFor1 isNotSaved
        ( buttonView "Save" Save ) <|
    ...

```

you might as well use square brackets, make it a list, and have only one set of definitions all based on `++`. The only exception is when the single item you’re trying to include is actually just a single token to the lexer. But that is so rare anyway. Almost always the single item will be something applied to something.

---

<div class="post-metadata">

**Author:** ![Chadtech](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/chadtech/32/609_2.png) [@Chadtech](https://discourse.elm-lang.org/u/Chadtech)\
**Post date:** [February 25, 2021, 4:52pm UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/14 "2021-02-25T16:52:26Z")

</div>

I feel strongly that Elm does not need something like `Fragment`. In Elm (but not React), there is nothing stopping you from writing a `view` function that returns a `List (Html msg)` instead of `Html msg`. You have complete freedom in Elm to write a view function however you would like, whereas in React I think you are practically limited to view components.

Im speaking from experience. I often write view functions that return lists.

So, taking @Janiczek 's OP example, I would just embrace the `List` type.

```elm
buttonsView : List (Html Msg)
buttonsView =
    [ buttonView "Foo" DoFoo
    , buttonView "Bar" DoBar
    , buttonView "Save" Save
    ]

```

```elm
buttonsView : { isSaved : Bool } -> List (Html Msg)
buttonsView {isSaved} =
  List.concat
    [ buttonView "Foo" DoFoo
    , buttonView "Bar" DoBar
    , saveButtons isSaved
    ]

saveButtons : Bool -> List (Html msg)
saveButtons isSaved =
    if isSaved then
        [ buttonView "Save as new" SaveAsNew
        , buttonView "Save changes" SaveChanges
        ]

    else 
        [buttonView "Save" Save]

```

I think the ergonomics of the list type are great. But another point I would like to make, is that I think wrapping views in arbitrary `<div>` leads to bugs. A lot of css rules are dictating the interaction between parent and child html notes, and when you have an arbitrary div in between, you are either changing or complicating the relationship between the things you were trying to position on the screen.

In more concrete terms, heres an example of an unfortunate situation I have been stuck with many times, where there are like 10 levels of html between the `<body>` and the _first_ part of what the UI is meant to render:

```auto
<div id="main">
    <div class="page-body">
        <div class="page-header">
        <div class="main-page-body-x"
            <div class="nav">
            <div class="page-container">
                <div class="page">
                    <div class="main-page-container">
                         <div class="main-page">
                            <div class="main-page-menu-bar">
                            ... 

```

when it could have been half the size, if developers werent in the routine of returning and wrapping `div`s.:

```auto
<div class="header">
<div class="body"
    <div class="nav">
     <div class="main-page"
         <div class="main-page-menu-bar">
                            ... 

```

Those sorts of super dom stacks inevitably lead to persistent problems with scroll bars being on the wrong element and areas of the page rendering either too short or beyond the page limits. Its just very difficult to manage the relationship between all those nodes. Its very difficult for developers to even conceptualize what the different layers are meant to do, which I think is reflected in the challenge of giving distinct names to several arbitrary layers (“main”, “root”, “container”, “page”, etc).

---

<div class="post-metadata">

**Author:** ![Janiczek](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/janiczek/32/4516_2.png) [@Janiczek](https://discourse.elm-lang.org/u/Janiczek)\
**Post date:** [February 25, 2021, 5:14pm UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/15 "2021-02-25T17:14:29Z")

</div>

I think I understand the gist but the examples don’t typecheck in my head. What’s the type annotation of buttonView - does it also return a List? Then we’re missing some List.concats somewhere?

Could you please make an Ellie so that I’m sure I’m not misunderstanding the idea? 🙂

---

<div class="post-metadata">

**Author:** ![Chadtech](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/chadtech/32/609_2.png) [@Chadtech](https://discourse.elm-lang.org/u/Chadtech)\
**Post date:** [February 25, 2021, 5:35pm UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/16 "2021-02-25T17:35:56Z")

</div>

Oh shoot. I made some type errors in my example.

Heres what I was aiming for, in this type-checked ellie: [https://ellie-app.com/ctgbXkwCcp2a1](https://ellie-app.com/ctgbXkwCcp2a1)

---

<div class="post-metadata">

**Author:** ![allanderek](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/allanderek/32/1360_2.png) [@allanderek](https://discourse.elm-lang.org/u/allanderek)\
**Post date:** [February 25, 2021, 7:59pm UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/17 "2021-02-25T19:59:13Z")

</div>

It’s great to hear from someone with an opposing view. Proposal threads often turn into something like an echo chamber, where everyone agrees the proposed feature is great/not-useful.

> [@Chadtech](#):
>
> In more concrete terms, heres an example of an unfortunate situation I have been stuck with many times, where there are like 10 levels of html between the `<body>` and the _first_ part of what the UI is meant to render:

I think this is the sort of state that @Janiczek is attempting to help **prevent**. I think the point is that these can occur _because_ there is no `Html.fragment`. So people are in a situation where their current function returns an `Html Msg`, but they have to add an element to it. Maybe they are returning a checkbox and realise that they should also return an associated `Html.label` element. Now perhaps what they **should** do is change the return type of the function to be `List (Html Msg)`, and then refactor at all the places that call it, but what they do instead is wrap it in a `Html.div [Html.Attributes.class "checkbox-container"]` and move on. What you end up with if you do that a few times is the kind of situation you describe where you have a bunch of divs with nothing in between the levels (like Chapter, sub-section, sub-sub-section headers with no text before inner most ‘sub-\*section’) If `Html.fragment` were a thing, then what they could do instead is just wrap the checkbox and label inside a `Html.fragment` and then they would still appear at the outer level with no "checkbox-container’ `div`.

I cannot quite shake the feeling that there is some hidden gotcha here, like maybe this means that basically **all** (view) functions return an `Html Msg` and we end up losing some important typing distinctions.

---

<div class="post-metadata">

**Author:** ![Chadtech](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/chadtech/32/609_2.png) [@Chadtech](https://discourse.elm-lang.org/u/Chadtech)\
**Post date:** [February 25, 2021, 8:40pm UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/18 "2021-02-25T20:40:24Z")

</div>

Okay I see. I think maybe I rushed into this topic too fast talking about the problems of overly layered html. We are all more or less aligned against that.

We’ve brought up `batch` functions in this thread. `Sub.batch`, `Cmd.batch`, and `Css.batch` seem to work pretty well in practice, so, maybe it wouldnt be so bad if there was an `Html.batch`. On the other hand, if Elm didnt have `Sub.batch` or `Cmd.batch` built in, then I think our next best alternative would be `update` functions returning `List (Cmd msg)` and `subscriptions : List (Sub msg)`; and we would have to use `++` and `List.concat` to merge groups of different `Cmd` or `Sub`. Which in my opinion is only slightly worse than what we have today with `batch`.

---

<div class="post-metadata">

**Author:** ![aguzubiaga](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/aguzubiaga/32/4117_2.png) [@aguzubiaga](https://discourse.elm-lang.org/u/aguzubiaga)\
**Post date:** [February 25, 2021, 9:33pm UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/19 "2021-02-25T21:33:24Z")

</div>

I think `display: contents` is supposed to eliminate the CSS side-effects of wrapping things in a `div`. From [the spec](https://www.w3.org/TR/css-display-3/#box-generation):

> For the purposes of box generation and layout, the element must be treated as if it had been replaced in the element tree by its contents

That said, it looks like not all browsers support it entirely: [CSS display: contents | Can I use... Support tables for HTML5, CSS3, etc](https://caniuse.com/css-display-contents)

---

<div class="post-metadata">

**Author:** ![joakin](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joakin/32/171_2.png) [@joakin](https://discourse.elm-lang.org/u/joakin)\
**Post date:** [February 26, 2021, 6:50pm UTC](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992/20 "2021-02-26T18:50:48Z")

</div>

> [@allanderek](#):
>
> Now perhaps what they **should** do is change the return type of the function to be `List (Html Msg)` , and then refactor at all the places that call it, but what they do instead is wrap it in a `Html.div [Html.Attributes.class "checkbox-container"]` and move on.

Rather than “perhaps” I’d say “often”, and use the friendly compiler and fearlessly refactor the code to fit the problem.

This seems like a social problem, and even if you have `Html.fragment`, why wouldn’t people keep doing what you just mentioned?

Good coding practices come from working with others, reviewing code, and healthy discussion. Linters like elm-review can help with these things some times too. There is no way around it.

I think Chad brings a great point, it is all functions and changing the return type helps with this.

* * *

I don’t have anything against the proposal, the impact in the virtual dom library is probably the most important thing to assess.

It would definitely be handy to have `fragment` because if you have functions that return `List (Html msg)` you can’t `map` over them like you can with a `Html msg`, which is very useful.

In the previous example you’d have to `saveButtons isSaved |> List.map (Html.map fn)` which is not the end of the world but not very ergonomic if you have to do it in many places with the `List.concat`s or `++`s.

[Next page](https://discourse.elm-lang.org/t/html-fragment-list-html-msg-html-msg/6992.md?page=2)
