How to build a filterable dropdown menu in Elm

Elm is a functional programming language that offers many benefits, such as code clarity, maintainability, and performance. It emphasizes immutability, strong static typing, and a declarative approach to building user interfaces. Based on the Model-View-Update pattern, Elm's architecture promotes a clear separation of concerns. It enables robust error handling, making it a powerful choice for building scalable and error-free web applications.

Note: More details about Elm can be found here.

Dropdown menu

The dropdown menu in this application provides a filtering feature where users can search for specific options by typing into an input field. The available options dynamically update based on the filter text. When a user selects an option, it is visually displayed within the dropdown menu. This functionality enhances the user experience by facilitating easy searching and selection of options.

  • Enhance user experience: The filterable dropdown menu can significantly improve the user experience of the application.

  • Streamline complex data: A filterable dropdown menu allows handling large datasets or complex data structures efficiently.

  • Simplify navigation: With a filterable dropdown menu, the navigation process for users will be simplified. Instead of scrolling through long lists, they can simply type in keywords or select specific filters to access the desired options directly.

  • Scalability and performance: Elm's architecture and functional programming principles make it an excellent choice for building performant and scalable applications.

Steps to build a filterable dropdown menu

Let's go through the steps to build a filterable dropdown menu in Elm.

Step 1: Setting up a new Elm project

  1. Open the terminal and navigate to the desired directory for our project. Running the following command will create a new Elm project with a basic file structure:

elm init
Command for initializing new Elm project
  1. Next, we need to install the necessary packages for our project. Open the elm.json file in the project directory and add the following packages to the "dependencies" section:

"elm/browser": "1.0.1",
"elm/core": "1.0.2",
"elm/html": "1.0.0",
"elm/time": "1.0.0",
"elm/http": "2.0.0",
"elm/json": "1.1.3"
Dependencies
  1. Save the elm.json file and run the following command to install the packages:

elm install
Command to install packages

Step 2: Create the necessary files

  1. Create a new file called Main.elm in the src directory. This file will contain our Elm module for the dropdown menu.

  2. Open the Main.elm file and start by declaring the module:

module Main exposing (..)
  1. Import the required modules:

import Browser
import Html exposing (Html, div, input, ul, li, text, button)
import Html.Attributes exposing (placeholder, class)
import Html.Events exposing (onInput, onClick)
import List exposing (filter)
import String exposing (toLower)
  1. Define the model for our dropdown menu:

-- MODEL
type alias Option =
{ id : Int
, label : String
, value : String
}
type alias Model =
{ options : List Option
, selectedOption : Maybe Option
, filterText : String
, isDropdownOpen : Bool
}
initialModel : Model
initialModel =
{ options =
[ { id = 1, label = "Option 1", value = "option1" }
, { id = 2, label = "Option 2", value = "option2" }
, { id = 3, label = "Option 3", value = "option3" }
]
, selectedOption = Nothing
, filterText = ""
, isDropdownOpen = False
}

Code explanation

  • Lines 4–8: The code begins by defining the Option type, which represents an individual option in the dropdown menu. It has three fields:

    • id: An integer representing the option's unique identifier.

    • label: A string representing the text label of the option.

    • value: A string representing the underlying value associated with the option.

  • Lines 11–16: The code defines the Model type, which represents the overall state of the dropdown menu. It has the following fields:

    • options: A list of Option values representing all the available options in the dropdown

    • selectedOption: A Maybe Option value representing the currently selected option, if any

    • filterText: A string representing the text entered in the filter input to filter the options

    • isDropdownOpen: A boolean value indicating whether the dropdown menu is open or closed

  • Lines 19–29: The initialModel function initializes the Model with default values. It sets the options field to a list of three options with predefined id, label, and value values. The selectedOption field is set to Nothing since no option is initially selected. The filterText field is set to an empty string, and the isDropdownOpen field is set to False indicating that the dropdown menu is initially closed.

  1. Define the messages that will update the model and implement the update function to handle the messages and update the model accordingly:

-- UPDATE
type Msg
= UpdateFilter String
| SelectOption (Maybe Option)
| ToggleDropdown
update : Msg -> Model -> Model
update msg model =
case msg of
UpdateFilter filterText ->
{ model | filterText = filterText }
SelectOption selectedOption ->
{ model | selectedOption = selectedOption }
ToggleDropdown ->
{ model | isDropdownOpen = not model.isDropdownOpen }

Code explanation

  • Lines 4–7: This code defines a type Msg which has three possible constructors: UpdateFilter, SelectOption, and ToggleDropdown.

  • Lines 10–20: The update function takes a Msg and a Model as input and returns a new Model as output.

    • If the Msg is of type UpdateFilter, it updates the filterText field of the Model with the given filterText.

    • If the Msg is of type SelectOption, it updates the selectedOption field of the Model with the given selectedOption.

    • If the Msg is of type ToggleDropdown, it toggles the isDropdownOpen field of the Model.

  1. Define the view function to render the dropdown menu:

-- VIEW
view : Model -> Html Msg
view model =
div []
[ input [ placeholder "Filter options", onInput UpdateFilter ] []
, ul [] (List.map (viewOption model) (filterOptions model))
]
viewOption : Model -> Option -> Html Msg
viewOption model option =
let
isSelected =
model.selectedOption == Just option
classValue =
if isSelected then
"selected"
else
""
in
li [ class classValue, onClick (SelectOption (Just option)) ]
[ text option.label ]
filterOptions : Model -> List Option
filterOptions model =
let
filterTextLower =
String.toLower model.filterText
in
List.filter (\option -> String.contains filterTextLower (String.toLower option.label)) model.options

Code explanation

  • Lines 4–9: The view function takes a Model as input and returns an Html Msg representation of the view. It consists of a div element containing an input field for filtering options and a list of filtered options.

  • Lines 12–25: The viewOption function takes a Model and an Option as input and returns an Html Msg representation of an individual option. It determines whether the option is selected by comparing it with the selectedOption field of the Model. If the option is selected, it assigns the CSS class "selected" to the li element; otherwise, it leaves the class empty.

  • Lines 28–34: The filterOptions function takes a Model as input and returns a filtered list of options based on the filterText field of the model. It converts the filterText to lowercase and uses it to filter the options' labels using the String.contains function.

  1. Now, at the end, we have to implement the MAIN function to execute the code:

-- MAIN
main =
Browser.sandbox { init = initialModel, update = update, view = view }

Code

Let's click the "Run" button to observe the functionality of our code:

{
    "type": "application",
    "source-directories": [
        "src"
    ],
    "elm-version": "0.19.0",
    "dependencies": {
        "direct": {
            "elm/browser": "1.0.1",
            "elm/core": "1.0.2",
            "elm/html": "1.0.0",
            "elm/time": "1.0.0",
            "elm/http": "2.0.0",
            "elm/json": "1.1.3"
        },
        "indirect": {
            "elm/url": "1.0.0",
            "elm/virtual-dom": "1.0.2",
            "elm/bytes": "1.0.8",
            "elm/file": "1.0.5"
        }
    },
    "test-dependencies": {
        "direct": {},
        "indirect": {}
    }
}
Filterable dropdown menu

Conclusion

Building a filterable dropdown menu in Elm provides a seamless way to enhance user interaction. Users can easily search and select options by incorporating filtering functionality, resulting in an improved user experience. The code example demonstrates how to implement a filterable dropdown menu using Elm, allowing users to filter options by typing in an input field. It also displays the selected option in the dropdown, providing a visually appealing and efficient user interface. With Elm's functional programming approach, the code ensures maintainability and scalability.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved