Skip to contents

muiCharts integrates with Shiny using shiny::uiOutput() and shiny::renderUI(). This article demonstrates common patterns for using muiCharts in Shiny applications.

Bootstrap Conflict

muiCharts uses MUI (Material UI) for rendering. Shiny’s default UI functions (fluidPage(), sidebarLayout(), etc.) and bslib both rely on Bootstrap CSS, which can conflict with MUI styling and cause visual issues in your charts.

To avoid conflicts, we recommend using muiMaterial for the general layout and UI inputs of your app. Since muiMaterial is also built on MUI, it shares the same design system as muiCharts and the two work together seamlessly.

You can still use other R Shiny packages (e.g., shiny, bslib, DT, plotly). However, if some UI elements look different than expected, it is likely due to CSS conflicts between Bootstrap and MUI.

Basic Usage

Render any muiCharts component inside renderUI():

library(muiCharts)
library(shiny)
library(dplyr)

film_counts <- starwars_films |>
  mutate(
    Characters = lengths(characters),
    Planets    = lengths(planets)
  ) |>
  arrange(episode_id)

ui <- tagList(uiOutput("chart"))

server <- function(input, output, session) {
  output$chart <- renderUI({
    BarChart(
      dataset = film_counts,
      layout  = "horizontal",
      yAxis   = list(list(scaleType = "band", dataKey = "title", width = 220)),
      series  = list(
        list(dataKey = "Characters", label = "Characters"),
        list(dataKey = "Planets",    label = "Planets")
      ),
      grid   = list(horizontal = TRUE),
      height = 380
    )
  })
}

shinyApp(ui, server)

BarChart

Reactive Charts

Use muiMaterial inputs to dynamically update charts — switch chart type and select which film elements to display:

library(muiCharts)
library(muiMaterial)
library(shiny)
library(dplyr)

film_counts <- starwars_films |>
  mutate(
    Characters = lengths(characters),
    Planets    = lengths(planets),
    Species    = lengths(species),
    Starships  = lengths(starships)
  ) |>
  arrange(episode_id)

ui <- muiMaterialPage(
  CssBaseline(
    Container(
      maxWidth = "md",
      sx = list(py = 4),
      Typography("Star Wars Films", variant = "h4", gutterBottom = TRUE),
      Stack(
        spacing = 3,
        ToggleButtonGroup.shinyInput(
          inputId   = "chart_type",
          value     = "bar",
          exclusive = TRUE,
          ToggleButton.shinyInput(inputId = "btn_bar",  value = "bar",  "Bar"),
          ToggleButton.shinyInput(inputId = "btn_line", value = "line", "Line")
        ),
        Box(
          Typography("Elements:", variant = "subtitle1"),
          FormGroup(
            row = TRUE,
            FormControlLabel(
              control = Checkbox.shinyInput(inputId = "chk_chars",     value = TRUE),
              label   = "Characters"
            ),
            FormControlLabel(
              control = Checkbox.shinyInput(inputId = "chk_planets",   value = TRUE),
              label   = "Planets"
            ),
            FormControlLabel(
              control = Checkbox.shinyInput(inputId = "chk_species",   value = FALSE),
              label   = "Species"
            ),
            FormControlLabel(
              control = Checkbox.shinyInput(inputId = "chk_starships", value = FALSE),
              label   = "Starships"
            )
          )
        ),
        uiOutput("chart")
      )
    )
  )
)

server <- function(input, output, session) {
  chart_type <- reactiveVal("bar")

  observeEvent(input$btn_bar,  chart_type("bar"))
  observeEvent(input$btn_line, chart_type("line"))

  selected_series <- reactive({
    keys <- c(
      Characters = input$chk_chars,
      Planets    = input$chk_planets,
      Species    = input$chk_species,
      Starships  = input$chk_starships
    )
    names(keys[keys == TRUE])
  })

  output$chart <- renderUI({
    req(length(selected_series()) > 0)
    series <- lapply(selected_series(), function(s) list(dataKey = s, label = s))

    if (chart_type() == "bar") {
      BarChart(
        dataset = film_counts,
        xAxis   = list(list(scaleType = "band", dataKey = "episode_id", label = "Star Wars Episode")),
        series  = series,
        height  = 400
      )
    } else {
      LineChart(
        dataset = film_counts,
        xAxis   = list(list(scaleType = "point", dataKey = "episode_id", label = "Star Wars Episode")),
        series  = series,
        height  = 400
      )
    }
  })
}

shinyApp(ui, server)

Click Events

Capture user clicks on chart elements using onAxisClick and Shiny.setInputValue():

library(muiCharts)
library(shiny)
library(dplyr)
library(glue)

film_counts <- starwars_films |>
  mutate(
    Characters = lengths(characters),
    Planets    = lengths(planets)
  ) |>
  arrange(episode_id)

ui <- tagList(
  tags$h1("Click on the chart"),
  uiOutput("chart"),
  tags$h3("You clicked:"),
  tableOutput("clickEvent")
)

server <- function(input, output, session) {
  setClickEvent <- function(inputId) {
    JS(glue::glue("(event, d) => Shiny.setInputValue('{inputId}', d)"))
  }

  output$clickEvent <- renderTable({
    includeNull <- function(x) ifelse(length(x) == 0, NA_character_, paste0(x))
    data.frame(
      episode    = includeNull(input$userClick$axisValue[1]),
      Characters = includeNull(input$userClick$seriesValues$`auto-generated-id-0`[1]),
      Planets    = includeNull(input$userClick$seriesValues$`auto-generated-id-1`[1])
    )
  })

  output$chart <- renderUI({
    BarChart(
      dataset    = film_counts,
      onAxisClick = setClickEvent("userClick"),
      xAxis      = list(list(scaleType = "band", dataKey = "episode_id", label = "Star Wars Episode")),
      series     = list(
        list(dataKey = "Characters", label = "Characters"),
        list(dataKey = "Planets",    label = "Planets")
      ),
      height = 300
    )
  })
}

shinyApp(ui, server)

ClickEvent

Dashboard with muiMaterial

Combine pie chart and gauge in a responsive dashboard layout:

library(muiCharts)
library(muiMaterial)
library(shiny)
library(dplyr)

gender_dist <- starwars_people |>
  count(gender) |>
  filter(!is.na(gender)) |>
  select(label = gender, value = n)

male_pct <- round(mean(starwars_people$gender == "male", na.rm = TRUE) * 100)

ui <- muiMaterialPage(
  CssBaseline(
    Container(
      maxWidth = "lg",
      sx = list(py = 4),
      Typography("Star Wars Metrics", variant = "h4", gutterBottom = TRUE),
      Box(
        Typography("Chart height:", variant = "subtitle1"),
        Slider.shinyInput(
          inputId          = "height",
          value            = 300,
          min              = 200,
          max              = 500,
          step             = 50,
          valueLabelDisplay = "auto",
          sx               = list(maxWidth = 300)
        )
      ),
      Grid(
        container = TRUE,
        spacing   = 3,
        sx        = list(mt = 2),
        Grid(
          size = list(xs = 12, md = 8),
          Card(CardContent(
            Typography("Characters by Gender", variant = "h6"),
            uiOutput("pieChart")
          ))
        ),
        Grid(
          size = list(xs = 12, md = 4),
          Card(CardContent(
            Typography("Male Characters", variant = "h6"),
            uiOutput("gauge")
          ))
        )
      )
    )
  )
)

server <- function(input, output, session) {
  output$pieChart <- renderUI({
    PieChart(
      series = list(list(data = gender_dist, innerRadius = 50, outerRadius = 100)),
      height = input$height
    )
  })

  output$gauge <- renderUI({
    Gauge(width = 150, height = 150, value = male_pct, startAngle = -90, endAngle = 90)
  })
}

shinyApp(ui, server)

Chart Dependency

muiChartsDependency() returns the HTML dependency for the MUI X Charts JavaScript library. Use it to manually add the dependency when embedding charts in custom HTML:

library(htmltools)

browsable(tagList(
  muiChartsDependency(),
  tags$div(id = "my-custom-chart")
))

reactOutput and renderReact

reactOutput() and renderReact() are alternatives to uiOutput() / renderUI() from the shiny.react package, re-exported here for convenience:

library(muiCharts)
library(shiny)
library(dplyr)

film_counts <- starwars_films |>
  mutate(Characters = lengths(characters)) |>
  arrange(episode_id)

ui     <- tagList(reactOutput("chart"))
server <- function(input, output, session) {
  output$chart <- renderReact({
    BarChart(
      dataset = film_counts,
      xAxis   = list(list(scaleType = "band", dataKey = "episode_id", label = "Star Wars Episode")),
      series  = list(list(dataKey = "Characters", label = "Characters")),
      height  = 300
    )
  })
}

shinyApp(ui, server)

setInput and triggerEvent

setInput() programmatically sets a Shiny input value from the server. triggerEvent() fires a named event to connected JavaScript handlers. Both are re-exported from shiny.react:

server <- function(input, output, session) {
  # Reset a Shiny input value programmatically
  observeEvent(input$reset, {
    setInput(session, inputId = "selectedIndex", value = 0L)
  })

  # Trigger a custom event for JavaScript-side chart interactions
  observeEvent(input$highlight, {
    triggerEvent(session, "chart:highlight", data = list(index = 0L))
  })
}