Here are a few ways to create buttons for downloading data in R shiny. As this might be the snake part that I know, feel free to write me if you use other methods.

https://sketchplanations.com

sketchplanations.com the blind and the elephant

1. shiny::downloadButton/shiny::downloadHandler (download on client side)

One straightforward way of creating a download button is to use shiny::downloadButton and shiny::downloadHandler. This downloads the data on the client side. For saving the data on the server side, see the example using the package shinyFiles below.

The donwloadHandler requires a function for the file content, which accepts only one argument, the file path. However, this function will be called in a context which knows the session information, so you can save your data for example in session$userData. Note also that the if the content function contains many lines of code, the server part might quickly be cluttered. In that case, you might want to use a function, as in the contrived example below.

.render_table <- function(output, data)
{
  output$mtcars_table <- DT::renderDataTable(
    server = TRUE,
    expr = {
      dt_options <- list(
        dom = "lftip",
        searchHighlight = TRUE,
        scrollX = TRUE,
        search = list(regex = TRUE, caseInsensitive = TRUE, smart = FALSE),
        language = list(searchPlaceholder = "case insensitive search")
      )
      
      out <- DT::datatable(
        data = data,
        rownames = FALSE,
        filter = list(position = "top", clear = FALSE),
        selection = list(mode = "single"),
        options = dt_options,
        class = "compact stripe nowrap"
      ) 
      
      out <- DT::formatStyle(
        table = out,
        columns = colnames(data),
        fontSize = "90%"
      )
      
      return(out)
    }
  )
}

.write_file <- function(file_path, data)
{
  data.table::fwrite(x = data, file = file_path)
}

ui <- shiny::fluidPage(
  DT::dataTableOutput(outputId = "mtcars_table"),
  
  shiny::downloadButton(
    outputId = "download_button", 
    label = "Download"
  )
)

server <- function(input, output, session) 
{
  session$userData$data <- mtcars
  
  .render_table(output = output, data = session$userData$data)
  
  output$download_button <- shiny::downloadHandler(
    filename = paste0("data-", Sys.Date(), ".csv"),
    content = function(file_path) 
    {
      .write_file(file_path = file_path, data = session$userData$data)
    }
  )
}

shinyApp(ui, server)

 

2. datatable buttons extension (download on client side)

Another possibility is to use the datatable buttons extension. Things to keep in mind:

  • make sure you use DT::renderDataTable(server = FALSE, ...) otherwise the downloaded data will only contain the data which the client currently sees, i.e. the page the client looks at
  • this method is not useful for large data files ('large' is relative, I can't give you a number, you'll get a datatable warning that the file is too large, and the app will be significantly slower)
.render_table <- function(output, data)
{
  download_file_prefix <- paste0("data_", toString(Sys.time()))
  download_file_prefix <- gsub(x = download_file_prefix, pattern = " |-|:", replacement = "_")
  
  output$mtcars_table <- DT::renderDataTable(
    server = FALSE,
    expr = {
      dt_options <- list(
        dom = "lftipB",
        searchHighlight = TRUE,
        scrollX = TRUE,
        search = list(regex = TRUE, caseInsensitive = TRUE, smart = FALSE),
        language = list(searchPlaceholder = "case insensitive search"),
        buttons = list(
          list(extend = "csv", filename = download_file_prefix),
          list(extend = "excel", filename = download_file_prefix)
        )
      )
      
      out <- DT::datatable(
        data = data,
        rownames = FALSE,
        filter = list(position = "top", clear = FALSE),
        selection = list(mode = "single"),
        options = dt_options,
        class = "compact stripe nowrap",
        extensions = "Buttons"
      ) 
      
      out <- DT::formatStyle(
        table = out,
        columns = colnames(data),
        fontSize = "90%"
      )
      
      return(out)
    }
  )
}

ui <- shiny::fluidPage(
  DT::dataTableOutput(outputId = "mtcars_table")
)

server <- function(input, output, session) 
{
  session$userData$data <- mtcars
  
  .render_table(output = output, data = session$userData$data)
}

shinyApp(ui, server)

3. Using shinyFiles::shinySaveButton and shinyFiles::shinyFileSave (dowload on the server side)

Data can be downloaded on the server side using the package shinyFiles. If there are security concerns, you might want to restrict the access based on shinyFiles::shinyFileSave(restrictions = ...).

.render_table <- function(output, data)
{
  output$mtcars_table <- DT::renderDataTable(
    server = TRUE,
    expr = {
      dt_options <- list(
        dom = "lftip",
        searchHighlight = TRUE,
        scrollX = TRUE,
        search = list(regex = TRUE, caseInsensitive = TRUE, smart = FALSE),
        language = list(searchPlaceholder = "case insensitive search")
      )
      
      out <- DT::datatable(
        data = data,
        rownames = FALSE,
        filter = list(position = "top", clear = FALSE),
        selection = list(mode = "single"),
        options = dt_options,
        class = "compact stripe nowrap"
      ) 
      
      out <- DT::formatStyle(
        table = out,
        columns = colnames(data),
        fontSize = "90%"
      )
      
      return(out)
    }
  )
}

.observe_save_button <- function(input, session)
{
  shiny::observeEvent(
    eventExpr = input$save,
    handlerExpr = {
      volumes <- c(Home = fs::path_home(), "R Installation" = R.home(), shinyFiles::getVolumes()())
      
      shinyFiles::shinyFileSave(
        input = input,
        id = "save",
        roots = volumes,
        session = session,
        restrictions = system.file(package = "base")
      )
      
      fileinfo <- shinyFiles::parseSavePath(
        roots = volumes, 
        selection = input$save
      )
      
      if (nrow(fileinfo) > 0) 
      {
        data.table::fwrite(x = session$userData$data, file = fileinfo$datapath)
      }
    }
  )
}

ui <- shiny::fluidPage(
  DT::dataTableOutput(outputId = "mtcars_table"),
  
  shinyFiles::shinySaveButton(
    id = "save",
    label = "save data",
    title = "save data as ...",
    filetype = list(csv = "csv"),
    viewtype = "icon"
  )
  
)

server <- function(input, output, session) 
{
  session$userData$data <- mtcars
  
  .render_table(output = output, data = session$userData$data)
  
  .observe_save_button(input = input, session = session)
}

shinyApp(ui, server)

The examples above were tested with R version 4.0.4, shiny version 1.6.0 and shinyFiles version 0.9.0.

 

Make a promise. Show up. Do the work. Repeat.