Molx Molx - 2 months ago 18
R Question

Export all user inputs in a Shiny app to file and load them later

My Shiny app has several inputs which are used to define several parameters of a generated plot. It's very likely that the user will spend some minutes going through all possible options until he's satisfied with the output. Obviously the plot can be exported in different formats, but it's possible that the user will want to recreate the same plot with different data later, or maybe just change one small detail.

Because of this, I need to offer the user a way to export all his settings and keep that file for later use. I've developed an approach, but it isn't working well. I'm using

to get the names of all input elements and save as a simple text file with the format
. This is the

output$bt_export <- downloadHandler(
filename = function() {
content = function(file) {
inputsList <- names(reactiveValuesToList(input))
exportVars <- paste0(inputsList, "=", sapply(inputsList, function(inpt) input[[inpt]]))
write(exportVars, file)

This works fine, but loading isn't going very smoothly. Since I don't (and couldn't figure out how) save the input type, I have to update the values blindly. This is how I do it:

importFile <- reactive({
inFile <- input$fileImport
if (is.null(inFile))
lines <- readLines(inFile$datapath)
out <- lapply(lines, function(l) unlist(strsplit(l, "=")))

imp <- importFile()
for (inpt in imp) {
if (substr(inpt[2], 0, 1) == "#") {
shinyjs::updateColourInput(session, inputId = inpt[1], value = inpt[2])
} else {
updateTextInput(session, inputId = inpt[1], value = inpt[2])
updateNumericInput(session, inputId = inpt[1], value = inpt[2])
updateSelectInput(session, inputId = inpt[1], selected = inpt[2])

Apart from the
, which can be recognized by the
start, I have to use
for the others. This works, partially, but some inputs are not being updated. Inspecting the exported file manually shows that inputs which weren't updated are there, so I suppose that updating 100+ inputs at once isn't a good idea. Also the
part doesn't look good and is probably not a good idea.

The app is close to finished, but will probably be updated in the future, having some inputs added/changed. It's acceptable if this even make some "old" exported inputs invalid, since I'll try keep the backwards compatibility. But I'm looking for an approach that isn't just writing hundreds of lines to update the inputs one-by-one.

I've thought about using
but simply using
does not restore the app inputs. I also considered a way to somehow update all inputs at once, instead of one-by-one, but didn't come up with anything. Is there any better way to export all user inputs to a file and then load them all? It doesn't matter if it's a tweak to this one that works better or a completely different approach.


If you look at the code of the shiny input update functions, they end by session$sendInputMessage(inputId, message). message is a list of attributes that need to be changed in the input, for ex, for a checkbox input: message <- dropNulls(list(label = label, value = value))

Since most of the input have the value attribute, you can just use the session$sendInputMessage function directly on all of them without the try.

Here's an example, I created dummy_data to update all the inputs when you click on the button, the structure should be similar to what you export:


            "This controls some of the labels:",
            "LABEL TEXT"),
  numericInput("inNumber", "Number input:",
               min = 1, max = 20, value = 5, step = 0.5),
  radioButtons("inRadio", "Radio buttons:",
               c("label 1" = "option1",
                 "label 2" = "option2",
                 "label 3" = "option3")),
  actionButton("update_data", "Update")




dummy_data <- c("inRadio=option2","inNumber=10","control_label=Updated TEXT" )

shinyServer(function(input, output,session) {
    out <- lapply(dummy_data, function(l) unlist(strsplit(l, "="))) 
   for (inpt in out) {
     session$sendInputMessage(inpt[1], list(value=inpt[2]))


All the update functions also preformat the value before calling session$sendInputMessage. I haven't tried all possible inputs but at least for these 3 you can pass a string to the function to change the numericInput and it still works fine.

If this is an issue for some of your inputs, you might want to save reactiveValuesToList(input) using save, and when you want to update your inputs, use load and run the list in the for loop (you'll have to adapt it to a named list).