How to Make a Professional-looking Shiny App and Not Get Intimidated (With R) | by Lathan Liou | Towards Data Science

This article is aimed towards people who have experience with R (ideally the tidyverse) and want to learn how to start making Shiny apps. For those who haven’t heard of Shiny before, it’s a package that allows you to create web applications using R without needing to know any HTML, CSS, or Javascript. That being said, if you do want to get deeper into app development, learning HTML, CSS, and Javascript will increase your ability to do more powerful things and have more control over the app development process. However, with access to so many tools, it can be overwhelming. So, this article aims to provide some baseline code and Shiny concepts to begin making a professional-looking Shiny app. It’s my hope that right off the bat, you will be able to produce a clean and high-quality app by following the steps in this article. And each step, if you’re curious, you can google more about it, and I think that’s one of the best ways to learn.
The motivation behind going straight into a more professional app as opposed to starting off with the boilerplate Shiny example is because the road to doing is often prolonged by theory and intermediate detours that may demotivate you. My philosophy is once you begin doing something, you’ll be able to 1) be proud of something advanced that you’ve done and 2) later fill in the details. I won’t go into too much detail about Shiny theory or some of the more complicated under-the-hood components of Shiny but a great resource is Hadley Wickham’s ongoing online textbook. So, for this article, bear with me, and it’ll take some effort on your part to look up any terms or concepts that I don’t explain comprehensively.
When you first install the shiny package and restart your RStudio workspace, you will see an option to initialize a shiny web app as so:
That will take you to a prompt that will give you a choice to either initialize the app in a single file (app.R) or in two files (ui.R and server.R). I will mention here that the backbone to any Shiny app is made up of two components: the UI (user interface) which defines how the app appears, and the server which defines how the app works. So for this example, we’re going to actually have three files (ui.R, server.R, and app.R, the last one is where we’ll load all our packages, source the UI and server scripts, and run the app). This three-script framework is often used for more complicated Shiny apps, and even though the sample app that I’ll take you guys through today is fairly simple, it’ll be good to start building good habits. So what you guys can do is go ahead and make three new R script files, and name them “ui.R”, “server.R”, and “app.R”.
Let’s start easy. Copy and paste the following code into your app.R file.
The main libraries that we’re going to be concerned with for the time being are shiny and shinydashboard. We’re using shinydashboardbecause it’s a neat package that provides a clean interface to present data and plots.
So, before we move on to the UI and server, let’s talk about what the app we’re building actually will be! We’ll be using the mtcars dataset and building a simple linear regression OLS model to predict mpg or miles per gallon. The app interface will allow the user to change various inputs, and the output will be a predicted mpg. Of course, the predicted mpg is, in of itself, meaningless since the model will have been overfit (we’re predicting on the same dataset for which we built the model), but that’s not our concern for this exercise.
A dashboard has three parts: a header, a sidebar, and a body. The most minimal example generates the following:
ui <- dashboardPage(  dashboardHeader(),  dashboardSidebar(),  dashboardBody())server <- function(input, output) { }shinyApp(ui, server)
So, for this app, I’m thinking of having the inputs in the sidebar (the black part) and the output in the body (the light blue part). With more complicated apps, it’s good practice to specify separate header, sidebar, and body objects. We’ll start with the header because it’s the simplest.
header <- dashboardHeader(title = “Demo Predictor”)
Next, let’s start to populate the sidebar interface with the inputs we want the user to be able to choose from; these will be the predictor variables in the mtcars dataset. For simplicity’s sake, we’ll make the categorical variables a selectInput, which will essentially provide the user with a dropdown menu of choices, and continuous variables a numericInput, which will allow the user to type in a number. These inputs basically take three key arguments (there are more but I’ll leave you to explore that on your own): the input ID (which we will use later when connecting the server to the UI), the label (the text that gets displayed in the app), and the value(s). Note that the input ID must be a simple string that contains only letters, numbers, and underscores (no spaces, dashes, periods, or other special characters allowed!) and must be unique. For more types of inputs, check out this chapter.
This is what it currently looks like:
So, a couple of comments here. So I’m using menu items within the sidebarMenu layout to essentially store what I want in a dropdown menu that can be shown/hidden upon click. The parameters I’m using are as follows (and of course there are many more):
menuItem(text = "Predict!", tabName = "model", icon = icon("bar-chart-o"),...)
where text is what is displayed and the name of the tab that the menu item will activate. Don’t worry too much about tabName for now, it’s mainly there if we want to have more control about how we want the menu item to interact with other parts of the dashboard or if we wanted to customize it further with HTML/CSS/Javascript. Note that the order in which you list the items in the code is the order in which the items appear. The other two components are the “Predict!” and “Clear” buttons.
Currently, if you were to press them, nothing would happen as we haven’t specified a server function for the two buttons, but ultimately “Predict!” will output a predicted mpg and “Clear” resets all inputs. Note that there is some code surrounding them that doesn’t look like R code at all. That’s because that is CSS (Cascading Style Sheets), which is a language to customize how website elements look. The code I’ve written essentially puts the two buttons side by side in the same row, but again, don’t worry too much about the particulars.
Lastly, let’s fill out the body.
Here, I’m introducing a verbatimTextOutput() which will print the predicted mpg. It is usually paired with renderText() which I’ll talk about later in the server code. Another important concept in Shiny UI design is the fluidRow(). A fluid row has a width of 12, so you can organize your content in columns or boxes in widths that add up to 12. The last concept is tags, but that is a little more advanced since tags allow you greater control over the HTML/CSS/Javascript elements. Here, just know that tags$style allows me to change CSS elements to customize things like font size and family.
Now, we have all our UI components, so we bring everything together like so.
ui <- dashboardPage(header,                     sidebar,                     body,                     useShinyjs())
What you have to keep in mind is that for any UI decision you make that has an input ID, you should pair a server function with it (that’s a general rule of thumb). So you’ll recall that we have all our mtcar variable inputs, the “Predict!” button, and the “Clear” button. The server functions are quite straightforward. We want to use all the variable inputs in a linear regression model. And we’re going to also write some separate code to reset the values.
This is where the input ID becomes important. It has to be unique because in the server, you reference the input using input$someID. There are three functions that I’ll break down:
observeEvent()
eventReactive()
output$pred <- renderText(pred())
To understand these, however, I have to talk about reactivity. Reactivity is a core component of Shiny that admittedly can be tough to wrap your head around initially. Essentially, the gist of reactivity is that any time a user changes an input, we do not want all our code to be rerun; we want to control when we rerun certain parts of code. This is important because imagine if each time you did an action like clicking or scrolling on a website, the website had to reload every time — that would be the most frustrating website ever! For a more in-depth description of reactivity, be sure to check out Hadley Wickham’s chapter on it.
observeEvent() basically tells the app to do an action, in this case resetting values back to the default upon a certain action, which in our case is the click of the actionButton() that we called “Clear” (with the ID “reset”). I use eventReactive() to essentially specify the code I want to run when a user hits the “Predict!” button, which is some fairly straightforward dplyr and base predict functions. I could even make this more efficient by performing the wrangling outside so that it doesn’t have to rerun every time, but for now, since it’s a small dataset, the code still runs very quickly. When I save an eventReactive()statement as an R object, to call it again, I have to treat it as if it were a function (this is Shiny’s way of reactive programming). That’s why predbecomes pred(). The last thing is rendering the output which we do with renderTextand we have to assign it to the output with the correct ID, so recall in our UI script, we specified verbatimTextOutput(pred).
This is what the app looks like when we run it shinyApp(ui, server).
Shiny is a really easy and user-friendly way for existing R users to get into website and app development. A Shiny app can be a powerful tool to convey your insights and allow users to also explore concepts and insights themselves. Using something like shinydashboard is really helpful because whether we realize it or not, the way an app is designed really makes all the difference. The more seamless and intuitive our experience feels, usually the more under-the-hood stuff there is that makes it all happen. For those of you who are interested this, I encourage using this app as a springboard and trying to incorporate new functionalities. Trying to learn all of HTML, CSS, and Javascript is overwhelming and definitely not recommended, so here are some of my recommended resources for where to look next:
Advanced Shiny Tips
How to Make your Shiny Dashboard Faster
Shinyjs
Hadley Wickham’s Shiny Textbook