Python Dash vs. R Shiny – Which To Choose in 2021 and Beyond
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Python and Dash vs. R and Shiny
Developing dashboards is no small task. You have to think about a vast amount of technical details and at the same time build something easy and enjoyable to use. Let’s say you have those areas covered. The question remains – which technology should you use? R or Python?
Today we’ll compare two technologies for building web applications – Python’s Dash and R’s Shiny. After reading this article, you’ll know how these two compare and when it’s better to use one over the other. You’ll also see if it’s worth it to make a long-term switch to either.
Don’t have the resources to build a custom solution in R or Python? Here are 3 Top BI tools that can be used as an alternative.
Here’s a list of topics covered throughout the article. Feel free to navigate to any section:
- Library Overview
- Boilerplate Comparison
- Creating UI Elements
- Styling UI with Custom CSS
- Styling UI with Bootstrap
- Reactivity
You can download the source code here.
At Appsilon, we are global leaders in R Shiny, and we’ve developed some of the world’s most advanced R Shiny dashboards, so we have a natural bias towards using Shiny. Still, we’ll do our best to provide an honest and unbiased opinion in this article. We’re not going to throw arbitrary points to Shiny just because we prefer it for enterprise app development.
It’s also worth noting that whether you choose Dash or Shiny (or both!), you can deploy your apps through RStudio Connect. With Connect, you can now share Flask APIs and interactive dashboards written in both R and Python. Appsilon is a Full-Service Certified RStudio Partner and can assist with deployment and app scaling regardless of your choice of underlying technology.
Library Overview
Let’s start with Python’s Dash. It is a Python framework used for building web applications. It’s written in Flask, Plotly.js, and React.js, so it’s an ideal candidate for creating dashboards. If you’re a heavy Python user, Dash will allow you to express your analysis quickly and visually.
Here’s an example dashboard you can create with Dash:
Want to see more dashboard examples? Check out Plotly’s official app gallery.
On the other hand, R Shiny is an open-source package for building web applications with R. It provides a robust web framework for developing any sort of apps, not only dashboards. Shiny is easy and intuitive to use, as you’ll see in the examples below. It also doesn’t require any HTML, CSS, or JavaScript knowledge – but this is helpful if you want extra customizability.
Here’s an example dashboard you can create with Shiny:
What else can you do with Shiny? Here’s our curated collection of demo Shiny dashboards.
Winner: Tie. You can develop identical solutions with both Dash and Shiny.
Boilerplate Comparison
Every web framework comes with a chunk of code needed for the application to run. You can’t avoid writing this code, or at least copying it from a previous project. That’s called boilerplate code. This section will show you just how much code is needed to start a Dash and a Shiny application. You will create two identical applications by the end, each showing only a single H3. Let’s begin with Dash.
import dash import dash_html_components as html app = dash.Dash(__name__) app.layout = html.Div(children=[ html.H3('Dash App') ]) if __name__ == '__main__': app.run_server(debug=True)
Here’s the corresponding application:
So eleven lines in total, and you haven’t imported any data visualization library. Three lines are empty – used for formatting purposes. In general – not bad. Let’s see what’s the deal with Shiny:
library(shiny) ui <- fluidPage( tags$h3("Shiny App") ) server <- function(input, output) { } shinyApp(ui, server)
Only nine lines here, of which three are empty. Here’s the corresponding application:
Winner: R Shiny. Does it really matter much, though? It’s only boilerplate code, after all. At this initial stage – no, it seems like it doesn’t matter. However, for more advanced applications, Dash requires a lot more boilerplate code than Shiny does. For instance, there are no reactive intermediate variables with Dash, which is a big drawback. We’ll return to this theme of Shiny ease-of-use throughout the article.
Creating UI Elements
Let’s continue our comparison by taking a look at UI elements. The goal is to create the same form-based application in both Dash and Shiny. The application should be used to filter job candidates by level, skills, and experience. It also allows you to specify additional points of interest.
Let’s start with Python’s Dash. All of the core UI components are available in the dash_core_componenets
library. The convention is to import it abbreviated as dcc
. Other imports and boilerplate remain the same.
Here’s the code for a simple form-based application:
import dash import dash_core_components as dcc import dash_html_components as html app = dash.Dash(__name__) app.layout = html.Div(children=[ html.H1('Heading 1'), html.Label('Filter by level:'), dcc.Dropdown( options=[ {'label': 'Junior', 'value': 'junior'}, {'label': 'Mid level', 'value': 'mid'}, {'label': 'Senior', 'value': 'senior'} ], value='junior' ), html.Label('Filter by skills:'), dcc.Dropdown( options=[ {'label': 'Python', 'value': 'python'}, {'label': 'R', 'value': 'r'}, {'label': 'Machine learning', 'value': 'ml'} ], value=['python', 'ml'], multi=True ), html.Label('Experience level:'), dcc.RadioItems( options=[ {'label': '0-1 years of experience', 'value': '01'}, {'label': '2-5 years of experience', 'value': '25'}, {'label': '5+ years of experience', 'value': '5plus'} ], value='25' ), html.Label('Additional:'), dcc.Checklist( options=[ {'label': 'Married', 'value': 'married'}, {'label': 'Has kids', 'value': 'haskids'} ], value=['married'] ), html.Label('Overall impression:'), dcc.Slider( min=1, max=10, value=5 ), html.Label('Anything to add?'), dcc.Input(type='text') ]) if __name__ == '__main__': app.run_server(debug=True)
And here’s the corresponding application:
Yeah, not the prettiest. Dash doesn’t include too many styles by default, so you’ll have to do it independently.
Let’s replicate the same application with R and Shiny:
library(shiny) ui <- fluidPage( tags$h1("Heading 1"), selectInput( inputId = "selectLevel", label = "Filter by level:", choices = c("Junior", "Mid level", "Senior"), selected = c("Junior") ), selectInput( inputId = "selectSkills", label = "Filter by skills:", choices = c("Python", "R", "Machine learning"), selected = c("Python", "Machine learning"), multiple = TRUE ), radioButtons( inputId = "radioExperience", label = "Experience level:", choices = c("0-1 years of experience", "2-5 years of experience", "5+ years of experience"), selected = c("2-5 years of experience") ), checkboxGroupInput( inputId = "cbxAdditional", label = "Additional:", choices = c("Married", "Has kids"), selected = c("Married") ), sliderInput( inputId = "slider", label = "Overall impression:", value = 5, min = 1, max = 10 ), textInput( inputId = "textAdditional", label = "Anything to add?" ) ) server <- function(input, output) { } shinyApp(ui, server)
Here’s the corresponding application:
As you can see, Shiny includes a ton more styling straight out of the box. Shiny applications look better than Dash applications by default. However, who wants their apps to look “default” anyway? We’ll cover custom styling in the next section.
Winner: R Shiny. You can create a better-looking application with less code.
Styling UI with Custom CSS
Nobody likes a generic-looking application. The aesthetics of your app are tied directly with how users feel about it. With advancements in design, we’re used to commercial apps looking spectacular. That doesn’t mean developing great-looking apps is easy, especially for developers.
Still, adding a touch of syle through CSS is more or less a must for your app. This section compares how easy it is to add styles to both Dash and Shiny if the same stylesheets look identical across the two. Who knows, maybe default stylings on Shiny will come as a drawback. Later, we’ll compare how easy it is to add custom CSS frameworks like Boostrap to your app.
A CSS file for the Dash application goes to the assets
folder and in the www
folder for Shiny apps. Create these folders in a root directory, where your application file is.
Here’s the complete CSS for both Dash and Shiny – called main.css
:
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap'); * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Lato', sans-serif; } .wrapper { padding: 1rem 2rem; background-color: #f2f2f2; height: 100vh; } .main-title { font-size: 3rem; } .paragraph-lead { font-size: 1.25rem; color: #777777; } .card { background-color: #ffffff; margin-top: 1.5rem; padding: 1rem 1.25rem; border: 0.1rem solid #c4c4c4; border-radius: 0.3rem; min-height: 12rem; } .card-title { margin-bottom: 1rem; } .card-text { margin-bottom: 3.5rem; } .card-button { background-color: #0099f9; color: #ffffff; font-weight: bold; border: 0.2rem solid #0099f9; border-radius: 0.5rem; padding: 1rem 1.5rem; cursor: pointer; transition: all 0.15s; } .card-button:hover { background-color: #ffffff; color: #0099f9; }
Let’s now use it to create a simple styled application – first with Dash:
import dash import dash_html_components as html app = dash.Dash(__name__) app.layout = html.Div( className='wrapper', children=[ html.H3('Dash App', className='main-title'), html.P('A simple Python dashboard', className='paragraph-lead'), html.Div( className='card', children=[ html.H3('A simple card', className='card-title'), html.P('Card text', className='card-text'), html.A('Button', className='card-button') ] ) ]) if __name__ == '__main__': app.run_server(debug=True)
Here’s the corresponding application:
As you can see, the stylings work just as for any regular web application. That’s because Dash didn’t add its default styles like Shiny did.
Here’s the code for a styled R Shiny app:
library(shiny) ui <- fluidPage( theme = "main.css", class = "wrapper", tags$h3("Shiny App", class = "main-title"), tags$p("A simple Shiny dashboard", class = "paragraph-lead"), tags$div( class = "card", tags$h3("A simple card", class = "card-text"), tags$p("Card text", class = "card-text"), tags$a("Button", class = "card-button") ) ) server <- function(input, output) { } shinyApp(ui, server)
And here’s the corresponding dashboard:
Well, that obviously doesn’t look right. The more elaborate default Shiny styling is conflicting with our custom styling. Shiny needs a bit more work with stylings than Dash, but that’s something you can quickly get used to. Still, the included styling with default Shiny apps means that there is a bit more work required to add custom styling to a Shiny app than a Dash app at a basic level.
Winner: Dash. This doesn’t mean you can’t make Shiny apps look fantastic, though.
Styling UI with Bootstrap
Let’s discuss CSS frameworks. Bootstrap’s been one of the most popular frameworks for years, so naturally, you might want to include it in your apps. It’s a great way to introduce new design elements and give your apps a fresher look. Your apps will still look kind of “default” – that’s mostly because Bootstrap has been out for so long and we’ve come to associate it with a “standard” appearance. Anyone who has been in web design/development for more than a short period knows this to be true.
Including Bootstrap in Dash is easy. You just have to install the dash_bootstrap_components
library and you’re good to go. The code below shows you how to create a navigation bar and a jumbotron in Dash:
import dash import dash_html_components as html import dash_bootstrap_components as dbc app = dash.Dash( external_stylesheets=[dbc.themes.BOOTSTRAP] ) navbar = dbc.NavbarSimple( children=[ dbc.NavItem(dbc.NavLink('About', href='#')), dbc.NavItem(dbc.NavLink('Products', href='#')), dbc.DropdownMenu( children=[ dbc.DropdownMenuItem('More pages', header=True), dbc.DropdownMenuItem('Page 2', href='#'), dbc.DropdownMenuItem('Page 3', href='#'), ], nav=True, in_navbar=True, label='More', ), ], brand='Bootstrap NavBar', brand_href='#', color='#0099f9', dark=True, ) jumbotron = dbc.Jumbotron([ html.H1('Welcome', className='display-3'), html.P('This is a sample jumbotron', className='lead'), html.Hr(className='my-2'), html.P('Additional dummy text'), html.P(dbc.Button('Learn more', color='primary'), className='lead'), ]) app.layout = html.Div([ navbar, jumbotron ]) if __name__ == '__main__': app.run_server(debug=True)
Here’s the corresponding application:
R Shiny is different. It comes with Bootstrap out of the box, but not with the most recent version. Bootstrap version 4.5 is the latest at this point, but Shiny is still stuck on 3.3.5. One solution for addressing this issue is by using the bslib
library. Here’s how to make the same application with Shiny:
As you can see, it is a code-heavy solution. It requires you to specify every CSS class and other properties as if you were writing in pure HTML. Declaring a winner in this department is a no-brainer.
Winner: Dash. Using Bootstrap is much cleaner in Dash than in Shiny.
Reactivity
Dashboards shouldn’t look and behave like plain reports. They have to be interactive. Users won’t like your dashboards if they can’t easily change what’s displayed and even interact with individual data points. That’s where reactivity (or callbacks) comes in. Options are endless here. You can update every component as a single input changes, update only selected components, delay updates until the button is pressed, etc. We’ll stick to the basics and perform the update when any of the inputs change.
For our example, you’ll have a single text input and two dropdown menus. Their values determine how the visualization looks. The text input is used to change the title, and the two dropdowns are used for attributes shown on the X and Y axes.
Let’s start with Python and Dash. Here’s the code:
import dash import dash_core_components as dcc import dash_html_components as html from dash.dependencies import Input, Output import plotly.express as px import statsmodels.api as sm import pandas as pd data = pd.DataFrame(sm.datasets.get_rdataset('mtcars', 'datasets', cache=True).data) external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] app = dash.Dash(__name__, external_stylesheets=external_stylesheets) app.layout = html.Div([ html.Label('Plot title:'), dcc.Input(id='input-title', value='MTCars visualization', type='text'), html.Br(), html.Label('X-axis:'), dcc.Dropdown( id='dropdown-x', options=[{'label': col, 'value': col} for col in data.columns], value='mpg' ), html.Br(), html.Label('Y-axis:'), dcc.Dropdown( id='dropdown-y', options=[{'label': col, 'value': col} for col in data.columns], value='mpg' ), html.Br(), html.Div( dcc.Graph(id='chart') ) ], style={'padding': '30px'}) @app.callback( Output('chart', 'figure'), Input('input-title', 'value'), Input('dropdown-x', 'value'), Input('dropdown-y', 'value')) def update_output(title, x_axis, y_axis): fig = px.scatter(data, x=x_axis, y=y_axis, title=title) return fig if __name__ == '__main__': app.run_server(debug=True)
And here’s the corresponding dashboard:
It’s a simple dashboard but requires a fair amount of code. Nevertheless, the code is simple to read and understand. Dash uses `@app.callback` decorator pattern to handle reactivity. You can update as many components in a single callback and get values from as many components as needed.
R Shiny is a bit simpler. It requires significantly less code to produce the same output. Still, the syntax might look strange if you’re not used to R (e.g., double exclamation point in front of column names).
Here’s the code for producing the identical dashboard:
library(shiny) library(ggplot2) library(plotly) ui <- fluidPage( tags$div( class = "wrapper", tags$style(type = "text/css", ".wrapper {padding: 30px}"), textInput(inputId = "inputTitle", label = "Plot title:", value = "MTCars visualization"), varSelectInput(inputId = "dropdownX", label = "X-axis", data = mtcars), varSelectInput(inputId = "dropdownY", label = "Y-axis", data = mtcars), plotlyOutput(outputId = "chart") ) ) server <- function(input, output) { output$chart <- renderPlotly({ col_x <- sym(input$dropdownX) col_y <- sym(input$dropdownY) p <- ggplot(mtcars, aes(x = !!col_x, y = !!col_y)) + geom_point() + labs(title = input$inputTitle) ggplotly(p) }) } shinyApp(ui, server)
Here’s the corresponding dashboard:
Winner: R Shiny. Shiny requires less code than Dash for better-looking output.
Conclusion
The final results are in:
- R Shiny – 3 points
- Python Dash – 2 points
- Tie – 1 point
It looks like R shiny is ahead by a single point. Does this mean that R Shiny better for everyone and every scenario? Absolutely not. You can develop identical production-ready applications in both technologies. What you choose depends on your specific needs and preferences.
R Shiny is a bit faster for developers. It requires less code than Dash for identical solutions. That’s if you decide to not use Bootstrap in your dashboards. Further, creating custom components in Dash requires extensive React.js knowledge, which is a large hurdle. Dash also falls short with intermediate reactive variables. Using them requires you to create a hidden div to store and reuse calculations. An alternative solution exists, but it’s still a big drawback when compared to R Shiny.
Learn More: Why You Should Use R Shiny for Enterprise Application Development
If you need to create powerful, customizable, and interactive dashboards that look great and respond to user input, Shiny is a clear choice. It requires a bit of coding knowledge even for simple dashboards, but R isn’t a very complicated language. You can quickly get up to speed in a couple of weeks or even a couple of days, depending on your prior knowledge of programming. If you want to make a scalable enterprise Shiny dashboard, then you can always reach out to Appsilon for help. We’re continually pushing the limits of what’s possible with Shiny, and we’d be happy to guide you and your company.
Learn More
- Tableau vs. R Shiny: Which Excel Alternative Is Right For You?
- PowerBI vs. R Shiny: Two Popular Excel Alternatives Compared
- How to Switch from Excel to R Shiny: First Steps
- Make R Shiny Dashboards Faster with updateInput, CSS, and JavaScript
Appsilon is hiring globally! We are primarily seeking an Engineering Manager who can lead a team of 6-8 ambitious software engineers. See our Careers page for all new openings, including openings for a Project Manager and Community Manager.
Article Python Dash vs. R Shiny – Which To Choose in 2021 and Beyond comes from Appsilon | End to End Data Science Solutions.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.