Figure 2: Steps to creating a Shiny Application project
The work environment
The virtual space where we create the shiny app. It contains packages, scripts, and data.
Packages
We need the package shiny. If you haven’t done so yet, please install now.
install.packages("shiny")library(shiny)
Source scripts
We might have code or custom functions saved in a separate r script file. This can be sourced into the app.
It helps with keep the workenvironment structured!
source("")
Load R data
We might have saved data after having manipulated it (cleaned, filtered etc) as an R data object. This can be loaded into the app.
It saves computing time but it is static meaning that you are limited to the saved data object itself. No access to original or raw data.
load("")
Sourcing code
In previous sessions, we wrote a basic function to add a constant to all numeric columns to a data frame.
We can copy this function into an .R script so that we have access to it in writing our first shiny app.
func1<-function(df,n){ tmp <-Filter(is.numeric, df) # we first filter the dataframe for numeric columns tmp + n # we then add the constant to all the numeric columns}
Source code
Other strategies in sourcing code include for example installing all packages from a separate r script file, or importing the data through a separate r script file.
Note that data importing is different than data loading!
Loading R data objects
Before we start working on the shiny app let us save the data (Stanciu et al., 2017) into an .Rdata format.
This makes it somewhat easier to import the dataframe in the shiny app code, as it is already in an r format.
# imports data in SPSS .sav format# make sure to actualize the path to the data file so that it matches your directorydfex<-haven::read_sav("data/sample.sav")# saves R object dfex into an .Rdata format # which we load into the shiny app shortlysave(dfex,file="data/sample.Rdata")
Once created, we can load the .Rdata object as follows:
load("data/sample.Rdata") # be sure to specify the path to your working directory
Save as .Rdata or .rds
Saving a dataframe into one of the r data formats makes it easier to write code for the app.
Use one of these file formats: .Rdata and .rds.
Shiny apps structure
One for the user, one for the server.
Shiny apps structure
### calls r script files# source("functions.R")### loads dataload("data/sample.Rdata")### imports librarieslibrary(shiny)r <-getOption("repos")r["CRAN"] <-"https://cloud.r-project.org/"options(repos=r)# install.packages("pacman")# pacman::p_load(tidyverse,readxl,haven,sjlabelled,kable,kableExtra)#### shiny app starts here #### Define UI for application that draws a histogramui <-fluidPage(# Application titletitlePanel("Illustrative example"),# Sidebar with a slider input for number of bins sidebarLayout(position ="left",sidebarPanel(# actor inputselectInput("actor",label="Choose an actor:",c("Keanu Reeves","Alec Baldwin","Arnold Schwarzenegger","Timothee Chalamet","Anamaria Marinca"),multiple =TRUE),# stereotype input selectInput("stereotype",label="Stereotype dimension:",c("Warmth women"="wom_warm","Competence women"="wom_comp","Warmth men"="men_warm","Competence men"="men_comp")) ),# Show a plot of the generated distributionmainPanel(h3("Output displayed here",),# start tabset PaneltabsetPanel(# tab 1tabPanel("Actors", tags$p(HTML(paste("A table is generated based on the actors chosen on the side panel..", sep ="")) ), DT::dataTableOutput("act") ),# tab 2tabPanel("Stereotypes", tags$p(HTML(paste("A plote is generated based on the variable chosen on the side panel..", sep ="")) ),plotOutput("st") ) ) # close tabset Panel ) ))# Define server logic required to draw a histogramserver <-function(input, output) {####### -- imports and prepares data from here # reactive object# data from Stanciu et al. 2017 tempdf <-reactive({ choice=input$stereotype dfex %>% sjlabelled::remove_all_labels() %>%pivot_longer(contains("warm") |contains("comp")) %>%filter(name %in% choice) })# reactive object# meta data movies movietmp<-reactive({ dfmv<-readxl::read_excel("mat/movies.xlsx",1) %>%filter(Actor %in% input$actor) })#### -- generates output objects from here# generate ggplot plottmp<-reactive({## ggplot code (input$plot_type =="ggplot2")ggplot(tempdf(), aes(x=factor(gen),y=value)) +labs(title=paste0("Evaluation based on ", input$stereotype), x="Gender",y=paste0("Stereotype of ", input$stereotype)) +geom_boxplot() +theme_light() })##### -- code for output from here# render plot for user output$st <-renderPlot({plottmp() }) output$act <- DT::renderDataTable({movietmp() })}# Run the application shinyApp(ui = ui, server = server)
Shiny apps have a user interface (UI) that is wrapped around code that runs in the background on a server.
When programming a shiny app therefore we need to program both the design (UI) and the code that runs on the server (server).
User Interface – UI
The UI part makes a shiny app attractive to the audience and, if programmed right, can engage the audience in an interactive and dynamic manner.
Programming the UI part requires a bit of orientation toward the audience for which the app is designed.
Pre-work
Before coding the app itself, think of these and similar questions:
What are the minimum skills required by your audience to operate the app?
What theoretical and practical expertise is expected from the audience to intuitively navigate the app?
In the UI part, we need refer to objects from the server part.
If we do not call objects from the server in the UI part properly, the app might still work but the audience will not have access to it.
Commas and brackets!
Make sure that you always use commas and close the brackets appropriately. Otherwise, the design might not look as intended or the entire code might break even.
Take some time to decide what do you want to include in the app and what do you need for your audience.
For example, do you want the audience to view plots or tables, and if yes, do you want these to be interactive?
What code do you need to write on the server part and what is the final r object that you’d want to be displayed for the audience via the UI?
Forth-back coding
Writing a shiny app is a bit of a forth and back between the UI and server code.
Layout
(a) Basic panels of a shiny app.
(b) Corresponding code in the UI.
Figure 3: UI code and corresponding shiny outcome.
sidebarPanel
sidebarPanel() wrapped inside the sidebarLayout() because it is just one element of several that can be placed on the sidebarLayout of the app.
The attributes defined here are fed into the code on the server, so make sure you chose the appropriate user input type.
sidebarLayout(position ="left",sidebarPanel(# actor inputselectInput("actor",label="Choose an actor:",c("Keanu Reeves","Alec Baldwin","Arnold Schwarzenegger","Timothee Chalamet","Anamaria Marinca"),multiple =TRUE), ...
mainPanel
mainPanel() contains the output, be it plain text, live text, tables or figures.
If in the sidebarPanel() you define the user input attributes, in the mainPanel() you simply call the objects computed on the server and programm how exactly will they be displayed.
mainPanel(h3("Output displayed here",),# start tabset PaneltabsetPanel(# tab 1tabPanel("Actors", tags$p(HTML(paste("A table is generated based on the actors chosen on the side panel..", sep ="")) ), DT::dataTableOutput("act") ),# tab 2tabPanel("Stereotypes", tags$p(HTML(paste("A plote is generated based on the variable chosen on the side panel..", sep ="")) ),plotOutput("st") ) ...
checkboxInput(inputId ="p1.ess",label ="Distribution", value = F)
How it looks
numeric
Code
numericInput(inputId ="numinp",label ="Number of obs",value =100,min =10,max =999,step =5)
How it looks
Conditional panels
There might be situations where you’d want to create a conditional user interface.
The UI experience can, at some pre-defined parts, be conditional on user input.
conditionalPanel( condition="input.type=='Simulations'",... # the code continues here with input values
How it looks
Tabset
There can be situations where it is helpful to organize output into separate panels – similar logic to having several tabs open on your web browser.
tabsetPabel(tabPanel1("label 1 for display to user", {content 1 here}),tabPanel2("label 2 for display to user", {content 2 here}))
How it looks
Server
The server part makes a shiny app, well, work.
Here is where code is written to import, clean, manipulate and analyse data, metadata and all sorts of other things.
One way that I find helpful to think of the server part is to see it as the old-school R coding on my local machine.
What does the server do?!
This distinction is less intuitive when we run the shiny app on the local machine. But, this distinction between UI and server becomes crucial when we deploy the app on online repositories, as we will see shortly.
Through structuring the app code in an UI and server part, we tell the respective online servers how to read and render the code.
Code for server
The code for the server is a custom function, a very large and complex one but, still a custom function.
Custom function
Remember, custom functions look like function(){}.
Input and output arguments
The server function takes two arguments:
input signals what comes from the UI interface. That is, what the user of the app is inputing via the UI.
output signals what goes from the server to the UI. That is, what the user views as a result of interacting with the app.
Reactive objects
Plain R code wrapped inside an object that the server needs to compute.
Reactive, because the server has to first compute it before performing any tasks that call on it.
Objects created on the server are reactive to code that requires them.
Mind the brackets
Remember to always call it as such. It is an r object all right, but it looks like a function: reactiveobject().
Code for the server
Needs to be written inside ({YOUR SERVER CODE HERE}).
It is a specific code chunk for the server.
# data from Stanciu et al. 2017 tempdf <-reactive({# define the input object for data manipulation choice=input$stereotype# takes the loaded data (a step not shown here)# then, manipulates the data# note that the output was not assigned to an object in the code# the output is assigned to the reactive object "tempdf()" dfex %>% sjlabelled::remove_all_labels() %>%pivot_longer(contains("warm") |contains("comp")) %>%filter(name %in% choice) })
Input objects
Reactive objects that take user input to generate output through code on the server.
# data from Stanciu et al. 2017 tempdf <-reactive({# define the input object for data manipulation choice=input$stereotype# takes the loaded data (a step not shown here)# then, manipulates the data# note that the output was not assigned to an object in the code# the output is assigned to the reactive object "tempdf()" dfex %>% sjlabelled::remove_all_labels() %>%pivot_longer(contains("warm") |contains("comp")) %>%filter(name %in% choice) })
Deploying the app might take some time but once done, the app will automatically open in your web browser.
Select files for deployment
Make sure you select for deployment only the files the app actually needs to function properly.
GitHub (optional)
One further thing we might want to do is to push the script files to GitHub. This, if we are working collaboratively or if we want the code to be publically available.
University of Luxembourg shiny server (optional)
As of April 2026, the University of Luxembourg has a functional shiny-server.
This hosting server is GDPR conform as everything is stored on the premises of UL.
A list of currently available apps hosted at UL can be found on the dedicated page: https://shiny-uni.uni.lu/
To deploy a shiny-app on this server:
Make sure you have an account on UL GitLab (open an IT ticket if not)
Get in touch with me (admin) to give you deployment rights
Push your repository to UL GitLab project shiny-uni
The server will automatically render the app and index it on the dedicated shiny server page
Note: Before you deploy your website, please send me a list of all r packages needed for your app. These packages will be installed for you on the server directly.
Coding a shiny app inside a quarto document makes your work environment more structured.
Inside a quarto document
From inside an .R script file
Ways to code a shiny application
Write a shiny app
Write a shiny app for your project.
Reference list
Friehs, M. T., Kotzur, P. F., Kraus, C., Schemmerling, M., Stanciu, A., & al, et. (2022). Warmth and competence perceptions of key protagonists are associated with containment measures during the COVID-19 pandemic: Evidence from 35 countries. Scientific Reports, 12, 21277. https://doi.org/10.1038/s41598-022-25228-9
Stanciu, A., Cohrs, C. J., Hanke, K., & Gavreliuc, A. (2017). Within-culture variation in the content of stereotypes: Application and development of the stereotype content model in an eastern european culture. The Journal of Social Psychology, 157(5), 611–628. https://doi.org/10.1080/00224545.2016.1262812
Witte, E. H., & Stanciu, A. (2023). Error theory of mental test scores without and with a measurement instrument. https://doi.org/10.31219/osf.io/9ap6m
Witte, E. H., Stanciu, A., & Zenker, F. (2022). Predicted as observed? How to identify empirically adequate theoretical constructs. Frontiers in Psychology, 13, 980261. https://doi.org/10.3389/fpsyg.2022.980261
Figure 2 (a): Step 1Figure 2 (b): Step 2Figure 3 (a): Basic panels of a shiny app.Figure 3 (b): Corresponding code in the UI.Inside a quarto documentFrom inside an .R script file