Go to the demo questionnaire and fill out the brief survey.
Create a new RStudio project for your data input apps.
Copy the following code to a new file and save it as app.R in a new directory in this project named inputdemo. Also create a directory named responses inside the inputdemo directory. Run the app with runApp('inputdemo').
library(shiny)
library(ggplot2)
outputDir <- "responses"
# Define the fields we want to save from the form
fields <- c("name", "used_shiny", "r_num_years")
saveData <- function(input) {
  # put variables in a data frame
  data <- data.frame(matrix(nrow=1,ncol=0))
  for (x in fields) {
    var <- input[[x]]
    if (length(var) > 1 ) {
      # handles lists from checkboxGroup and multiple Select
      data[[x]] <- list(var)
    } else {
      # all other data types
      data[[x]] <- var
    }
  }
  data$submit_time <- date()
    
  # Create a unique file name
  fileName <- sprintf(
    "%s_%s.rds", 
    as.integer(Sys.time()), 
    digest::digest(data)
  )
  
  # Write the file to the local system
  saveRDS(
    object = data,
    file = file.path(outputDir, fileName)
  )
}
loadData <- function() {
  # read all the files into a list
  files <- list.files(outputDir, full.names = TRUE)
  
  if (length(files) == 0) {
    # create empty data frame with correct columns
    field_list <- c(fields, "submit_time")
    data <- data.frame(matrix(ncol = length(field_list), nrow = 0))
    names(data) <- field_list
  } else {
    data <- lapply(files, function(x) readRDS(x)) 
    
    # Concatenate all data together into one data.frame
    data <- do.call(rbind, data)
  }
  
  data
}
deleteData <- function() {
  # Read all the files into a list
  files <- list.files(outputDir, full.names = TRUE)
  
  lapply(files, file.remove)
}
resetForm <- function(session) {
  # reset values
  updateTextInput(session, "name", value = "")
  updateCheckboxInput(session, "used_shiny", value = FALSE)
  updateSliderInput(session, "r_num_years", value = 0)
}
ui <- fluidPage(
  
  # App title ----
  titlePanel("Data Collection & Feedback"),
  
  # Sidebar layout with input and output definitions ----
  sidebarLayout(
    
    # Sidebar panel for inputs ----
    sidebarPanel(
      textInput("name", "Name", ""),
      checkboxInput("used_shiny", "I've built a Shiny app before", FALSE),
      sliderInput("r_num_years", "Number of years using R",
                  0, 10, 0, ticks = FALSE),
      actionButton("submit", "Submit"),
      actionButton("clear", "Clear Form"),
      downloadButton("downloadData", "Download"),
      actionButton("delete", "Delete All Data")
    ),
    
    # Main panel for displaying outputs ----
    mainPanel(
      plotOutput(outputId = "yearsPlot"),
      tags$hr(),
      dataTableOutput("responses")
    )
  )
)
server = function(input, output, session) {
  
  # When the Submit button is clicked, save the form data
  observeEvent(input$submit, {
    saveData(input)
    resetForm(session)
  })
  
  observeEvent(input$clear, {
    resetForm(session)
  })
  
  # When the Delete button is clicked, delete all of the saved data files
  observeEvent(input$delete, {
    deleteData()
  })
  
  # Show the previous responses in a reactive table ----
  output$responses <- renderDataTable({
    # update with current response when Submit or Delete are clicked
    input$submit 
    input$delete
    loadData()
  })
  
  # Downloadable csv of selected dataset ----
  output$downloadData <- downloadHandler(
    filename = "data.csv",
    content = function(file) {
      write.csv(loadData(), file, row.names = FALSE, quote= TRUE)
    }
  )
  
  output$yearsPlot <- renderPlot({
    input$submit
    input$delete
    
    data <- loadData()
    
    ggplot(data) +
      geom_histogram(
        aes(r_num_years), 
        binwidth = 1, 
        color = "black", 
        fill = "white"
      ) +
      scale_x_continuous(
        name = "Number of years using R", 
        breaks = 0:10,
        limits = c(-0.5, 10.5)
      ) + 
      theme_minimal() +
      theme(
        text = element_text(family = "Helvetica", size = 20),
        plot.background = element_rect(fill = "white"),
        panel.grid = element_blank(),
        axis.title.y = element_blank()
      )
  })
}
shinyApp(ui, server)
This framework gives you a full-page questionnaire with no feedback panel. You can use this framework or the one above.
library(shiny)
library(ggplot2)
outputDir <- "responses"
# Define the fields we want to save from the form
fields <- c("text_demo", 
            "select_demo", 
            "password_demo", 
            "textarea_demo", 
            "slider_demo", 
            "checkbox_demo", 
            "cbgroup_demo", 
            "date_demo", 
            "daterange_demo", 
            "number_demo")
saveData <- function(input) {
  # put variables in a data frame
  data <- data.frame(matrix(nrow=1,ncol=0))
  for (x in fields) {
    var <- input[[x]]
    if (length(var) > 1 ) {
      # handles lists from checkboxGroup and multiple Select
      data[[x]] <- list(var)
    } else {
      # all other data types
      data[[x]] <- var
    }
  }
  data$submit_time <- date()
  
  # Create a unique file name
  fileName <- sprintf(
    "%s_%s.rds", 
    as.integer(Sys.time()), 
    digest::digest(data)
  )
  
  # Write the file to the local system
  saveRDS(
    object = data,
    file = file.path(outputDir, fileName)
  )
}
loadData <- function() {
  # read all the files into a list
  files <- list.files(outputDir, full.names = TRUE)
  
  if (length(files) == 0) {
    # create empty data frame with correct columns
    field_list <- c(fields, "submit_time")
    data <- data.frame(matrix(ncol = length(field_list), nrow = 0))
    names(data) <- field_list
  } else {
    data <- lapply(files, function(x) readRDS(x)) 
    
    # Concatenate all data together into one data.frame
    data <- do.call(rbind, data)
  }
  
  data
}
# Define questions
select_demo <- selectInput(
  "select_demo", 
  "Complete these famous lyrics:  
  \"I ***** ***** ***** down in Africa\"", 
  c("", 
    "bless the waves", 
    "sense the rain", 
    "bless the rain", 
    "guess it rains"
  )
)
radio_demo <- radioButtons(
  "radio_demo", 
  "Do you like Toto?",
  c("yes", "no"), 
  inline = TRUE
)
checkbox_demo <- checkboxInput("checkbox_demo", "I consent to more 80s music references")
cbgroup_demo <- checkboxGroupInput(
  "cbgroup_demo", 
  "Which artists had a UK number one single in the 80s?",
  c("Pat Benatar" = "pb",
    "Toto" = "toto",
    "Blondie" = "blon",           # atomic 1980-03-01
    "Kraftwerk" = "kw",           # computer love 1982-02-06
    "Dog Faced Hermans" = "dfh",
    "Eurythmics" = "eur",         # there must be an angel 1985-07-27
    "T'Pau" = "tpau"              # china in your hand 1987-11-14
  )
)
number_demo <- numericInput(
  "number_demo", 
  "How many UK number one songs did Madonna have in the 80s?", 
  min = 0, max = 20, step = 1, value = 0 # answer = 6
)
slider_demo <- sliderInput(
  "slider_demo", 
  "How would you rate the 80s musically, on a scale from 0-100?",
  min = 0, max = 100, step = 1, value = 50
)
date_demo <- dateInput(
  "date_demo", 
  "Africa by Toto reached its peak position of #3 in the UK charts on what date?",
  min = "1980-01-01", max = "1989-12-31", startview="decade"
  # right answer is 1983-02-26
)
daterange_demo <- dateRangeInput(
  "daterange_demo", 
  "What was the full UK Top 100 chart run of Africa by Toto?",
  min = "1980-01-01", max = "1989-12-31", startview="decade"
  # right answer is 1983-01-29 to 1983-04-09
)
text_demo <- textInput("text_demo", "What is your favourite 80s band?")
textarea_demo <- textAreaInput("textarea_demo", "What do you think about this exercise?")
password_demo <- passwordInput("password_demo", "Tell me a secret.")
action_demo <- actionButton("clear", "Clear Form")
download_demo <- downloadButton("download", "Download")
file_demo <- fileInput("file_demo", "Upload a PDF", accept = "pdf")
help_demo <- helpText("You can write help text in your form this way")
resetForm <- function(session) {
  updateTextInput(session, "text_demo", value = "")
  updateSelectInput(session, "select_demo", selected=character(0))
  updateRadioButtons(session, "radio_demo", selected = "yes")
  updateCheckboxInput(session, "checkbox_demo", value = FALSE)
  updateCheckboxGroupInput(session, "cbgroup_demo", selected=character(0))
  updateTextAreaInput(session, "textarea_demo", value = "")
  updateTextInput(session, "password_demo", value = "")
  updateSliderInput(session, "slider_demo", value = 50)
  updateDateInput(session, "date_demo", value = NA)
  updateDateRangeInput(session, "daterange_demo", start = NA, end = NA)
  updateNumericInput(session, "number_demo", value = 0)
}
# Set up questionnaire interface ----
ui <- fluidPage(
  title = "Questionnaire Framework",
  # CSS ----
  # stop the default input containers being 300px, which is ugly
  tags$head(
    tags$style(HTML("
                    .shiny-input-container:not(.shiny-input-container-inline) {
                      width: 100%;
                      max-width: 100%;
                    }
                    "))
    ),
  
  # App title ----
  h3("My Survey"),
  
  p("Please fill out the following brief survey..."),
  
  fluidRow(
    column(width=6, text_demo),
    column(width=6, password_demo)
  ),
  
  fluidRow(
    column(width=4,
           select_demo,
           radio_demo,
           checkbox_demo
    ),
    column(width=4, 
           cbgroup_demo
    ),
    column(width=4, 
           number_demo
    )
  ),
  
  slider_demo,
  date_demo,
  daterange_demo,
  textarea_demo, 
  
  actionButton("submit", "Submit"),
  action_demo
)
# Reactive functions ----
server = function(input, output, session) {
  
  # When the Submit button is clicked, save the form data
  observeEvent(input$submit, {
    saveData(input)
    resetForm(session)
    
    # thank the user
    n_responses <- length(list.files(outputDir))
    response <- paste0("Thank you for completing the survey! You are respondant ",
                      n_responses, ".")
    showNotification(response, duration = 0, type = "message")
  })
  
  # clear the fields
  observeEvent(input$clear, {
    resetForm(session)
  })
}
shinyApp(ui, server)
In the section for data input (sidebarPanel or fluidRow), you can add a widget for each question, then some action buttons for submitting the data or other actions. The Shiny Widgets Gallery is a useful reference for choosing the right input widgets for your questions.
saveData <- function(input) {
  # put variables in a data frame
  data <- data.frame(matrix(nrow=1,ncol=0))
  for (x in fields) {
    var <- input[[x]]
    if (length(var) > 1 ) {
      # handles lists from checkboxGroup and multiple Select
      data[[x]] <- list(var)
    } else {
      # all other data types
      data[[x]] <- var
    }
  }
  data$submit_time <- date()
  
  # Create a unique file name
  fileName <- sprintf(
    "%s_%s.rds", 
    as.integer(Sys.time()), 
    digest::digest(data)
  )
  
  # Write the file to the local system
  saveRDS(
    object = data,
    file = file.path(outputDir, fileName)
  )
}
Use the observeEvent function to define what happens when you click the submit button. It goes inside the server function. The following code handles multiple selections and checkbox groups (which are very tricky to store in a table) and adds a submit_time column.
  # When the Submit button is clicked, save the form data
  observeEvent(input$submit, {
    saveData(input)
    resetForm(session)
  })
You’re done with a basic questionnaire at this point if you don’t want to give feedback to the participant. If you aren’t showing feedback, you can reassure the user that their response was submitted with code like showNotification(response, duration = 0, type = "message").
First, you need to create a function for loading and conatenating all of the previously saved data files.
loadData <- function() {
  # read all the files into a list
  files <- list.files(outputDir, full.names = TRUE)
  
  if (length(files) == 0) {
    # create empty data frame with correct columns
    field_list <- c(fields, "submit_time")
    data <- data.frame(matrix(ncol = length(field_list), nrow = 0))
    names(data) <- field_list
  } else {
    data <- lapply(files, function(x) readRDS(x)) 
    
    # Concatenate all data together into one data.frame
    data <- do.call(rbind, data)
  }
  
  data
}
Then, you can write a function that displays feedback calculated from the loaded data. This feedback shows an interactive table of all the collected data, whenever the submit or detele buttons are pressed.
    # Show the previous responses in a reactive table ----
    output$responses <- renderDataTable({
      # update with current response when Submit or Delete are clicked
      input$submit 
      input$delete
      
      loadData()
    })
If you don’t have access to an R server, you can sign up for a free account at shinyapps.io. Get your token and secret and add them to the code below.
rsconnect::setAccountInfo(
  name='my-username', 
  token='my-token', 
  secret='my-secret'
)
rsconnect::deployApp("inputdemo")