Python Dash vs. R Shiny – Which To Choose in 2021 and Beyond

This article was first published on python – Appsilon | End­ to­ End Data Science Solutions , and kindly contributed to python-bloggers. (You can report issue about the content on this page here)
Want to share your content on python-bloggers? click here.
Shiny vs Dash Thumbnail

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:

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:

New York Uber Rides Dash Dashboard

Image 1 – New York Uber Rides Dash Dashboard

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:

Shiny Enterprise Dashboard

Image 2 – Shiny Enterprise Dashboard

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:

Basic Dash application

Image 3 – Basic Dash 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:

Basic Shiny application

Image 4 – Basic Shiny 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:

Simple form-based application in Python's Dash

Image 5 – Simple form-based application in Python’s Dash

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:

Simple form-based application in R Shiny

Image 6 – Simple form-based application in R Shiny

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:

Styled Dash application

Image 7 – Styled Dash 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:

Styled Shiny application

Image 8 – Styled Shiny application

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:

Dash and Bootstrap

Image 9 – Dash and Bootstrap

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:

Shiny and Bootstrap

Image 10 – Shiny and Bootstrap

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:

Reactivity demonstration with Python and Dash

Image 11 – Reactivity demonstration with Python and Dash

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:

Reactivity demonstration with R Shiny

Image 12 – Reactivity demonstration with R Shiny

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

 

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.

To leave a comment for the author, please follow the link and comment on their blog: python – Appsilon | End­ to­ End Data Science Solutions .

Want to share your content on python-bloggers? click here.