As I continue to build more Elm applications, I find myself creating small utility functions to overcome common pain points. I’m a big fan of abstracting away repetitive patterns into reusable, well-named functions. Today, I’m happy to announce the release of my first public Elm package: html-helpers.
What’s the Problem? Link to heading
Elm’s HTML API is quite straightforward - you create HTML elements as functions, pass them attributes and children, and compose them together. But a few cases come up repeatedly that can be awkward:
- Conditional rendering: Showing or hiding multiple elements based on some condition often leads to boilerplate code
- Empty elements: You end up writing
text ""
a lot when you need to render nothing - Wrapping elements: When a function returns a list of HTML nodes, but the caller needs a single node
These aren’t major issues, but they’re little friction points that come up repeatedly.
Enter html-helpers Link to heading
The package contains just three simple functions (for now), but each addresses a common pain point when working with HTML in Elm:
1. contentList
: Conditional rendering made easy
Link to heading
The most obvious usecase is similar to that of Attributes.classList
, but for content rather than classes:
viewAnimals : Animal -> Animal -> Animal -> Html msg
viewAnimals dog cat mouse =
Html.section [ Attributes.id "animals" ] <|
contentList
[ ( Html.text dog.name, dog.visible ) -- dog will be rendered if dog.visible is True
, ( Html.text cat.name, cat.visible ) -- etc
, ( Html.text mouse.name, mouse.visible ) -- etc
]
This is much cleaner than the alternatives:
-- Without contentList, using List.filter and List.map
Html.section [ Attributes.id "animals" ] <|
([ ( Html.text dog.name, dog.visible )
, ( Html.text cat.name, cat.visible )
, ( Html.text mouse.name, mouse.visible )
]
|> List.filter Tuple.second
|> List.map Tuple.first
)
-- Or using List.concat and if expressions
Html.section [ Attributes.id "animals" ] <|
List.concat
[ if dog.visible then [ Html.text dog.name ] else []
, if cat.visible then [ Html.text cat.name ] else []
, if mouse.visible then [ Html.text mouse.name ] else []
]
2. wrapToSingleNode
: Working with lists of nodes
Link to heading
Sometimes you have a function that returns a list of HTML elements, but you need to return a single node. The common pattern is to wrap them in a div
, but that adds an extra DOM element that might affect your styling.
wrapToSingleNode
uses the following logic:
- If the list is empty, return
nothing
- If the list contains only one element, simply return that element
- If the list contains multiple elements, wrap them with a plain div with
display: contents
Using display: contents
means the wrapper div doesn’t create a new box in the layout - its children are treated as if they were direct children of the wrapper’s parent.
view : Model -> Html Msg
view model =
div []
[ h1 [] [ text "My App" ]
, -- This function returns a list of nodes
viewItems model.items
|> wrapToSingleNode
, footer [] [ text "Footer" ]
]
viewItems : List Item -> List (Html Msg)
viewItems items =
List.map viewItem items
3. nothing
: A clearer way to express emptiness
Link to heading
This is just a convenience function that replaces the common pattern of text ""
with a more semantically clear name:
viewMaybe : Maybe String -> Html msg
viewMaybe maybeStr =
case maybeStr of
Just str ->
p [] [ text str ]
Nothing ->
nothing -- Clearer than text ""
How to Use the Package Link to heading
Install the package using elm’s package manager:
elm install cekrem/html-helpers
Then import the functions you need:
import HtmlHelpers exposing (contentList, wrapToSingleNode, nothing)
The Road to 1.0 Link to heading
This is a small package focused on solving specific pain points. I’d like to gather feedback from the community before potentially adding a few more helper functions. If you find these helpers useful, or if you have ideas for other HTML helpers that would be valuable, please let me know!
Why Create a Package? Link to heading
You might wonder why these simple functions warrant publication as a package. There are a few reasons:
- Reusability: I found myself copying these functions between projects
- Documentation: Publishing forces me to properly document the code
- Community: Other Elm developers might find them useful too
- Learning: The process of creating and publishing a package was a learning experience
The Elm ecosystem values small, focused packages that do one thing well. Even with just three functions, this package can make your Elm code a little cleaner and more expressive.
Final Thoughts Link to heading
I hope you find these helpers useful in your Elm projects. The package is open source under the BSD-3-Clause license, and contributions are welcome. Check it out at package.elm-lang.org/packages/cekrem/html-helpers/latest/.
For those interested in the implementation details, the source is available on GitHub.