Flexdashboards
Interactive Web applications with R
Welcome back everyone! Today we will have a look at how to build interactive dashboards with flexdashboard and maps with leaflet.
Dashboards with flexdashboard
The goal of flexdashboard is to make it easy to create interactive dashboards for R, using R Markdown. A flexdashboard can either be static (a standard web page) or dynamic (a Shiny interactive document). A wide variety of components can be included in flexdashboard layouts, for a full list see the documentation.
As with Shiny apps, we are going to be working with the example in
the folder /flexdash. We will use a slightly adapted
version of this flexdashboard created by Philipp
Ottolinger.
Flexdashboard in practice 🏀
The big difference to Shiny apps is that the flexdashboard Rmd file
will take over the function used to define the layout of the
dashboard/app. In other words, the entire Rmd, now plays the role of
fluidPage().
So let’s have a closer look at how you can structure a flexdashboard.
Columns and rows
Dashboards are divided into columns and rows, with output components
delineated using level 3 markdown headers (###). By
default, dashboards are laid out within a single column, with charts
stacked vertically within a column and sized to fill available browser
height.
If you want to change the dashboard to include multiple columns you
can introduce the following (--------------).
If you want to change the dashboard to include multiple tabs you can
use {.tabset}.
In the YAML tab of the Rmd you can specify a number of additional properties:
vertical_layout: scrollorientation: rows
Feeling overwhelmed? No need to come up with a fancy design all on your own. You can find a long list of templates here.
Shiny Components
To add Shiny components to a flexdashboard, you need
runtime: shiny in your YAML header and the
shiny library loaded.
Input-Output Basics
You probably know this by now, but as a reminder: Shiny uses a reactive system where inputs (widgets) automatically trigger outputs (visualizations) to update.
Common inputs:
selectInput()- dropdownssliderInput()- sliderscheckboxInput()- checkboxes
Common outputs:
renderPlot()- plotsrenderLeaflet()- mapsrenderTable()- tables
Each input needs an ID (first argument). Access it in outputs using
input$ID_NAME.
Where Things Go
In a standalone Shiny app, you’d define inputs in
the ui and outputs in the server function. In
flexdashboard, it’s simpler: - Inputs
go in a column with the {.sidebar} attribute -
Outputs go in regular content panes (chart
sections)
Quick Example
# Input (in sidebar column)
selectInput("selected_year", "Choose Year:", choices = 2000:2010)
# Output (in chart section) - automatically updates when input changes
renderPlot({
data |>
filter(year == input$selected_year) |>
ggplot(aes(x = category, y = value)) +
geom_col()
})
A Real Example: Nuclear Waste Sites
Let’s look at a complete flexdashboard to see how these pieces work together. This dashboard visualizes nuclear waste sites across the United States, showing their cleanup status and locations.
What This Dashboard Does
The dashboard has three main components:
- A main map showing all sites color-coded by cleanup status.
- A bar chart showing the count of sites in each status category.
- A focused map highlighting sites located on university campuses.
Data preparation in the setup chunk: loading the JSON data, unnesting nested data frames, creating color schemes, and inferring whether sites are on campuses. This runs once when the dashboard loads, not repeatedly.
Layout structure: the dashboard uses a two-column
layout with data-width attributes to control sizing. The
main map gets more space (650) while the sidebar charts get less
(350).
Interactive elements:both maps use
popup to show site details on click. The bar chart uses
plotly::ggplotly() to convert a static ggplot into an
interactive plotly chart with hover tooltips.
Your Turn: Gapminder Dashboard
Time to build your own interactive flexdashboard! You’ll create a dashboard exploring global development data from the gapminder package.
Open the file our-flex/gapminder_flex.Rmd in the course
materials. The file shows a basic map of Africa. Here are your
tasks:
Exercise 1
Load and explore the data - The
gapminderpackage is already loaded. Inspect the datasetgapminderwithglimpse()to see the structure. Then, create a new code chunk for data preparation. Filter gapminder for African countries only and year 2007.Join the datasets - Join your filtered gapminder data to the
africaboundaries (already loaded in setup). Figure out which columns to use for the join.Visualize life expectancy with colors - In the map code chunk, use your joined dataset in the
leaflet()function. Color the polygons by adding thefillColorargument toaddPolygons(). The structure isfillColor = ~pal(variable_name). The palettepalis predefined in the setup chunk. SetfillOpacity = 0.7to make the colors visible.Finally, add a legend - Check out the nuclear waste sites dashboard for inspiration. Use
addLegend()and specify:pal(the color palette),values(the variable you’re mapping, with the format~variable_name),title(what to call it), andposition(try “bottomleft”).
Phew! That was a lot. But now you’ve got a working map with real data. Let’s make it interactive!
Exercise 2
Make your map interactive so users can select different years and see how life expectancy changed over time.
Add a year selector - Create a sidebar section with
Column {.sidebar}and add aselectInput()widget. Give it an id (you’ll use this to access the selected value), a label like “Select Year:”, and usechoices = unique(gapminder$year)to populate all available years.Make the map reactive - Wrap your map code in
renderLeaflet({ }). Move your data filtering inside this function and change the year filter to useinput$your_id_nameinstead of a hardcoded year like 2007.Test it - Knit the document and try changing the year selector. The map should update automatically!
Hint: Remember that code using input$
values needs to be inside a render*() function to be
reactive. Move your data filtering and joining inside
renderLeaflet(), but keep the africa
boundaries loading outside (it only needs to happen once).
Other Useful Widgets
Beyond leaflet maps, flexdashboard supports many interactive visualization libraries:
Interactive tables: - DT::datatable() -
sortable, filterable, searchable tables with pagination -
reactable::reactable() - modern interactive tables with
customization
Interactive plots: - plotly::ggplotly()
- convert ggplot2 plots to interactive plotly charts -
plotly::plot_ly() - create plotly charts directly
For more examples and widgets, see the HTML widgets showcase.
Acknowledgements
The running example of the flexdashboard was adapted from Waste Lands - America’s forgotten nuclear legacy.
This script was drafted by Tom Arendt and Lisa Oswald, with contributions by Steve Kerr, Hiba Ahmad, Carmen Garro, and Sebastian Ramirez-Ruiz.