Skip to contents

Lists are continuous, vertical indexes of text or images. They are composed of items containing primary and supplemental actions, which are represented by icons and text.

Basic list

A simple list with text items:

muiMaterialPage(
  CssBaseline(
    Box(
      sx = list(width = "100%", maxWidth = 360, bgcolor = "background.paper"),
      List(
        ListItem(
          ListItemButton(
            ListItemIcon(shiny::icon("inbox")),
            ListItemText(primary = "Inbox")
          )
        ),
        ListItem(
          ListItemButton(
            ListItemIcon(shiny::icon("paper-plane")),
            ListItemText(primary = "Drafts")
          )
        )
      )
    )
  )
)

Nested list

A list with nested items and collapsible sections:

library(shiny)
library(muiMaterial)

muiMaterialPage(
  CssBaseline(
    Box(
      sx = list(width = "100%", maxWidth = 360, bgcolor = "background.paper"),
      List(
        component = "nav",
        ListItemButton(
          ListItemIcon(shiny::icon("home")),
          ListItemText(primary = "Home")
        ),
        ListItemButton(
          ListItemIcon(shiny::icon("envelope")),
          ListItemText(primary = "Inbox"),
          shiny::icon("chevron-down"),
          onClick = JS("() => {
            const el = document.getElementById('inbox-collapse');
            if (el) {
              el.style.display = el.style.display === 'none' ? 'block' : 'none';
            }
          }")
        ),
        Collapse(
          `in` = TRUE,
          timeout = "auto",
          unmountOnExit = TRUE,
          id = "inbox-collapse",
          List(
            component = "div",
            disablePadding = TRUE,
            ListItemButton(
              sx = list(pl = 4),
              ListItemIcon(shiny::icon("star")),
              ListItemText(primary = "Starred")
            ),
            ListItemButton(
              sx = list(pl = 4),
              ListItemIcon(shiny::icon("clock")),
              ListItemText(primary = "Snoozed")
            )
          )
        ),
        ListItemButton(
          ListItemIcon(shiny::icon("paper-plane")),
          ListItemText(primary = "Drafts")
        )
      )
    )
  )
)

List with dividers

Use Divider() to separate list items:

muiMaterialPage(
  CssBaseline(
    Box(
      sx = list(width = "100%", maxWidth = 360, bgcolor = "background.paper"),
      List(
        component = "nav",
        ListItem(
          ListItemButton(
            ListItemIcon(shiny::icon("inbox")),
            ListItemText(primary = "Inbox")
          )
        ),
        Divider(),
        ListItem(
          ListItemButton(
            ListItemIcon(shiny::icon("paper-plane")),
            ListItemText(primary = "Drafts")
          )
        ),
        Divider(),
        ListItem(
          ListItemButton(
            ListItemIcon(shiny::icon("trash")),
            ListItemText(primary = "Trash")
          )
        )
      )
    )
  )
)

List with avatars

Combine lists with Avatar() components:

muiMaterialPage(
  CssBaseline(
    Box(
      sx = list(width = "100%", maxWidth = 360, bgcolor = "background.paper"),
      List(
        ListItem(
          ListItemAvatar(
            Avatar(
              alt = "Remy Sharp",
              src = "https://mui.com/static/images/avatar/1.jpg"
            )
          ),
          ListItemText(
            primary = "Remy Sharp",
            secondary = "Available for chat"
          )
        ),
        ListItem(
          ListItemAvatar(
            Avatar(
              alt = "Travis Howard",
              src = "https://mui.com/static/images/avatar/2.jpg"
            )
          ),
          ListItemText(
            primary = "Travis Howard",
            secondary = "Busy - Do not disturb"
          )
        ),
        ListItem(
          ListItemAvatar(
            Avatar(
              alt = "Cindy Baker",
              src = "https://mui.com/static/images/avatar/3.jpg"
            )
          ),
          ListItemText(
            primary = "Cindy Baker",
            secondary = "Away"
          )
        )
      )
    )
  )
)

List with secondary action

Add secondary actions to list items using ListItemSecondaryAction():

muiMaterialPage(
  CssBaseline(
    Box(
      sx = list(width = "100%", maxWidth = 360, bgcolor = "background.paper"),
      List(
        ListItem(
          secondaryAction = IconButton(
            edge = "end",
            shiny::icon("comment")
          ),
          ListItemAvatar(
            Avatar(
              alt = "Remy Sharp",
              src = "https://mui.com/static/images/avatar/1.jpg"
            )
          ),
          ListItemText(
            primary = "Remy Sharp",
            secondary = "Jan 9, 2024"
          )
        ),
        ListItem(
          secondaryAction = IconButton(
            edge = "end",
            shiny::icon("comment")
          ),
          ListItemAvatar(
            Avatar(
              alt = "Travis Howard",
              src = "https://mui.com/static/images/avatar/2.jpg"
            )
          ),
          ListItemText(
            primary = "Travis Howard",
            secondary = "Jan 7, 2024"
          )
        )
      )
    )
  )
)

Interactive list with reactRouter

Create an interactive navigation list with reactRouter R package:

library(muiMaterial)
library(reactRouter)
#> 
#> Attaching package: 'reactRouter'
#> The following object is masked from 'package:muiMaterial':
#> 
#>     Link

muiMaterialPage(
  CssBaseline(
    HashRouter(
        Box(
        sx = list(display = "flex", height = "100vh"),
        Box(
            sx = list(width = 240, bgcolor = "background.paper", borderRight = "1px solid #e0e0e0"),
            List(
                component = "nav",
                NavLink(
                    to = "/",
                    style = "text-decoration: none; color: black",
                    ListItemButton(
                        ListItemIcon(shiny::icon("home")),
                        ListItemText(primary = "Home")
                    )
                ),
                NavLink(
                    to = "/inbox",
                    style = "text-decoration: none; color: black",
                    ListItemButton(
                        onClick = Navigate("/inbox"),
                        ListItemIcon(shiny::icon("inbox")),
                        ListItemText(primary = "Inbox")
                    )
                ),
                NavLink(
                    to = "/starred",
                    style = "text-decoration: none; color: black",
                    ListItemButton(
                        onClick = Navigate("/starred"),
                        ListItemIcon(shiny::icon("star")),
                        ListItemText(primary = "Starred")
                    )
                ),
                NavLink(
                    to = "/drafts",
                    style = "text-decoration: none; color: black",
                    ListItemButton(
                        ListItemIcon(shiny::icon("paper-plane")),
                        ListItemText(primary = "Drafts")
                    )
                )
            )
        ),
        Box(
            sx = list(flex = 1, p = 3),
            Routes(
            Route(
                path = "/",
                element = Typography("Home Page", variant = "h4")
            ),
            Route(
                path = "/inbox",
                element = Typography("Inbox", variant = "h4")
            ),
            Route(
                path = "/starred",
                element = Typography("Starred", variant = "h4")
            ),
            Route(
                path = "/drafts",
                element = Typography("Drafts", variant = "h4")
            )
            )
        )
        )
    )
  )
)
#> Warning: The `reloadDocument` argument of `Link()` default is now FALSE as of
#> reactRouter 0.2.0.
#>  The default of `reloadDocument` was TRUE in version 0.1.1. It is now FALSE.
#> This warning is displayed once per session.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.

Selectable list

Create a list with selectable items:

library(shiny)
library(muiMaterial)

ui <- muiMaterialPage(
  CssBaseline(
    Box(
      sx = list(width = "100%", maxWidth = 360, bgcolor = "background.paper", p = 2),
      Typography("Select items:", variant = "h6", sx = list(mb = 2)),
      uiOutput("selectable_list"),
      Typography(
        uiOutput("selected_message"),
        variant = "body2",
        sx = list(mt = 2)
      )
    )
  )
)

server <- function(input, output, session) {
  items <- c("Photos", "Work", "Vacation")
  selected <- reactiveVal(character(0))
  
  output$selectable_list <- renderUI({
    List(
      lapply(items, function(item) {
        is_selected <- item %in% selected()
        ListItem(
          disablePadding = TRUE,
          ListItemButton(
            selected = is_selected,
            onClick = JS(sprintf(
              "() => Shiny.setInputValue('toggle_item', {item: '%s', time: Math.random()})",
              item
            )),
            ListItemIcon(shiny::icon("folder")),
            ListItemText(primary = item)
          )
        )
      })
    )
  })
  
  observeEvent(input$toggle_item, {
    item <- input$toggle_item$item
    if (item %in% selected()) {
      selected(setdiff(selected(), item))
    } else {
      selected(c(selected(), item))
    }
  })
  
  output$selected_message <- renderUI({
    if (length(selected()) == 0) {
      "No items selected"
    } else {
      paste("Selected:", paste(selected(), collapse = ", "))
    }
  })
}

shinyApp(ui, server)

List with checkboxes

Add checkboxes to list items for multiple selection:

library(shiny)
library(muiMaterial)

ui <- muiMaterialPage(
  CssBaseline(
    Box(
      sx = list(width = "100%", maxWidth = 360, bgcolor = "background.paper", p = 2),
      Typography("To-do list:", variant = "h6", sx = list(mb = 2)),
      uiOutput("checkbox_list")
    )
  )
)

server <- function(input, output, session) {
  todos <- reactiveVal(list(
    list(id = "1", text = "Buy groceries", checked = FALSE),
    list(id = "2", text = "Walk the dog", checked = FALSE),
    list(id = "3", text = "Finish report", checked = FALSE),
    list(id = "4", text = "Call mom", checked = FALSE)
  ))
  
  output$checkbox_list <- renderUI({
    List(
      lapply(todos(), function(todo) {
        ListItem(
          disablePadding = TRUE,
          ListItemButton(
            role = "undefined",
            onClick = JS(sprintf(
              "() => Shiny.setInputValue('toggle_todo', {id: '%s', time: Math.random()})",
              todo$id
            )),
            dense = TRUE,
            ListItemIcon(
              Checkbox(
                edge = "start",
                checked = todo$checked,
                tabIndex = -1,
                disableRipple = TRUE
              )
            ),
            ListItemText(
              primary = todo$text,
              sx = if (todo$checked) list(textDecoration = "line-through") else NULL
            )
          )
        )
      })
    )
  })
  
  observeEvent(input$toggle_todo, {
    todo_id <- input$toggle_todo$id
    current <- todos()
    updated <- lapply(current, function(t) {
      if (t$id == todo_id) {
        t$checked <- !t$checked
      }
      t
    })
    todos(updated)
  })
}

shinyApp(ui, server)

List with switches

Add switches to list items for settings-style lists:

library(shiny)
library(muiMaterial)

ui <- muiMaterialPage(
  CssBaseline(
    Box(
      sx = list(width = "100%", maxWidth = 360, bgcolor = "background.paper", p = 2),
      Typography("Notifications:", variant = "h6", sx = list(mb = 2)),
      uiOutput("settings_list")
    )
  )
)

server <- function(input, output, session) {
  settings <- reactiveVal(list(
    list(id = "wifi", label = "Wi-Fi", enabled = TRUE),
    list(id = "bluetooth", label = "Bluetooth", enabled = FALSE),
    list(id = "notifications", label = "Notifications", enabled = TRUE)
  ))
  
  output$settings_list <- renderUI({
    List(
      lapply(settings(), function(setting) {
        ListItem(
          ListItemIcon(
            if (setting$id == "wifi") shiny::icon("wifi")
            else if (setting$id == "bluetooth") shiny::icon("bluetooth")
            else shiny::icon("bell")
          ),
          ListItemText(primary = setting$label),
          Switch(
            edge = "end",
            checked = setting$enabled,
            onChange = JS(sprintf(
              "() => Shiny.setInputValue('toggle_setting', {id: '%s', time: Math.random()})",
              setting$id
            ))
          )
        )
      })
    )
  })
  
  observeEvent(input$toggle_setting, {
    setting_id <- input$toggle_setting$id
    current <- settings()
    updated <- lapply(current, function(s) {
      if (s$id == setting_id) {
        s$enabled <- !s$enabled
      }
      s
    })
    settings(updated)
  })
}

shinyApp(ui, server)

Dense list

Use the dense prop for more compact lists:

muiMaterialPage(
  CssBaseline(
    Box(
      sx = list(width = "100%", maxWidth = 360, bgcolor = "background.paper"),
      List(
        dense = TRUE,
        ListItem(
          ListItemAvatar(
            Avatar(
              alt = "Remy Sharp",
              src = "https://mui.com/static/images/avatar/1.jpg"
            )
          ),
          ListItemText(
            primary = "Remy Sharp",
            secondary = "Software Engineer"
          )
        ),
        ListItem(
          ListItemAvatar(
            Avatar(
              alt = "Travis Howard",
              src = "https://mui.com/static/images/avatar/2.jpg"
            )
          ),
          ListItemText(
            primary = "Travis Howard",
            secondary = "Product Manager"
          )
        ),
        ListItem(
          ListItemAvatar(
            Avatar(
              alt = "Cindy Baker",
              src = "https://mui.com/static/images/avatar/3.jpg"
            )
          ),
          ListItemText(
            primary = "Cindy Baker",
            secondary = "Designer"
          )
        )
      )
    )
  )
)