Streamlit Review

Last updated:
January 20, 2021

Total Score: 

78

None

Streamlit launched in 2018 to help data scientists build data apps. The marketing emphasizes development speed with a pure Python approach, and our testing proved that it’s the real deal. In short, if you want to get a front-end for your data science project up and running as quickly as possible, Streamlit is the clear winner. The downside of the framework’s elegance is that fine-grained customization is difficult if not impossible. If you want complete control of layout, aesthetics, user interactions, and compute architecture, Streamlit will feel frustratingly confined. On balance, however, Streamlit’s fast development speed and “good-enough” functionality make it our #1 recommendation.

Demo

Because Streamlit is our top recommendation, we have deployed our Streamlit prototype as a live demo. For more on how we chose this design, please see the Methodology tab in the main article.

Streamlit demo app screenshot
Our prototype Streamlit data app.


Highlights

Surprise and delight Friction and frustration
It just works, right out-of-the-box. Nice defaults for functionality and aesthetics. Can't build complex interactive patterns. No way to define custom callback functions.
Documentation is very crisp: precisely what you need to get up and running quickly. Limited control over the layout.
The built-in screencast recorder is handy and works well.


Detailed score breakdown

Please see the Methodology tab for details about how we define each of the following dimensions and the relative weight that we attach to each one.

Ease of learning and development

The first thing we noticed when starting out with Streamlit is how crisp and clean the documentation is. Just enough detail to get a nice-looking app running, but no more. Even the config file is well-documented.

Streamlit’s API design is equally elegant. Define a simple hierarchy of layout containers (the reactive sidebar is included automatically), then add plots and control widgets with imperative-style commands. Elements appear on the canvas in the order they appear in code unless otherwise specified.

Streamlit re-runs the whole Python script whenever any app state changes. This enables an impressive level of interactivity without the need for custom callback functions. Widget objects in the code "just work"; they take whatever value the user chooses in the live app.

Lastly, the default aesthetics are excellent and don't need any tweaking. All told, it took us just a few hours to go from zero to a finished prototype, which meant we could spend more time thinking about the data science methods and narrative.

There are a couple of small rough edges. Debugging is clunky; we found ourselves running the app script in an IPython terminal to debug, side-by-side with the rendered version in a browser. We also were not able to get the “magic” plot commands to work (e.g. streamlit.bar_chart). The workaround is pretty straightforward, though: first, create an Altair chart object, then attach it to a layout element.

Functionality

Streamlit’s strong opinions about layout and compute model maximize development speed and get you to a “good-enough” functionality very quickly. In our opinion, this is exactly the right balance to strike, but it will leave power users frustrated.

In particular, Streamlit does not allow the app author to define custom reactive behavior. In our design, for example, we wanted the time series plots to double as user controls for filtering the data by date. This does not seem possible in Streamlit.

Streamlit's insistence on re-running the whole app script when any app state changes means the slow data loading and preprocessing steps are repeated many times, unnecessarily. Streamlit’s function caching tool mitigates this, but the details are a bit murky.

Layout options are limited with Streamlit. The built-in sidebar is very handy—we spent way too much time in other frameworks tweaking the layout of control widgets—but the only other way to organize content is by columns.

We are very happy with the way Altair plots came out in our demo. Plotly plots did not work as smoothly; the figures would not resize automatically to the width of their container, despite our best efforts.

Stability and vibrancy

Streamlit the company seems to be on solid ground, having raised $27 million in venture capital over two rounds in 2019 and 2020. The product is gaining traction with data scientists; as of January 2021, it has 12,600 stars on Github, 240 tagged questions in StackOverflow, and nearly 70 posts per week in Streamlit’s own forum.

As with any startup, it's hard to forecast future acquisitions that could impact product availability, but the app creation codebase is free and open-source on Github, under a permissive Apache 2.0 license.

Bells and whistles

Streamlit excels in the bells and whistles department: the built-in screencast tool works seamlessly; you can automatically sync an app script to an S3 bucket; and you can run an app script from a URL (e.g. a Github gist), which is a decent fall-back option for sharing if you can’t deploy from a server.

Predictability

There a handful of gotchas to watch for:

  • Streamlit installs a lot of dependencies.

  • By default, Streamlit sends usage statistics back to the mothership. This can be turned off in the config.

  • By default, Streamlit watches the entire file system for changes in case a running app needs to be refreshed. The documentation suggests this can be changed, although we didn’t confirm it.

Remaining questions

Things we haven’t yet had a chance to test ourselves:

  • Caching: how effective is it? How much time does it save, and for which kinds of functions? How does it work for simultaneous readers?

  • Dependencies: What do all the dependencies do? How secure are they?

  • Hosting: how well does Streamlit’s hosted service work? How much does it cost?

Building the demo

To build our prototype, we relied primarily on Streamlit's getting started tutorial. Streamlit has strong opinions, presumably intended to maximize development speed. One is that layout is controlled imperatively, with a hierarchy of spatial containers. For example, we start our Python script by adding the app title and description directly to the Streamlit module object:1

import altair as alt
import pandas as pd
import streamlit as st

st.title("Judicial Nominations")
st.write("The nomination of Amy Coney Barrett to the ...")

A sidebar exists by default. Adding control widgets—a dropdown selector and buttons, in this case—follows the same pattern:

cohort_var = st.sidebar.selectbox(
    "Select Cohort Field", ["congress", "president", "residence", "role"], index=0
)

all_levels_button = st.sidebar.button("Select all cohort levels")
top_levels_button = st.sidebar.button("Select most frequent cohort levels")

Creating two columns in the main body for plots is similar. In this case, we want the right column to be slightly wider than the left.

left_col, right_col = st.beta_columns([1, 1.25])

Streamlit's second strong opinion is that the whole Python script is re-run whenever any app state changes. This means we don't have to write our own callback functions, but it also means the slow steps of dataset loading and preprocessing are repeated many times. To avoid this, Streamlit lets us decorate functions with a cache operator that stores function outputs in a fast lookup table. For illustration, we use a data loader function that creates a toy dataset; in the complete demo, this function loads data from a CSV file and does all of the preprocessing.

@st.cache
def load_data_cached():
    # Toy data
    df = pd.DataFrame(
        {
            "congress": [108, 108, 114, 114],
            "last_action": ["confirmed", "returned", "confirmed", "returned"],
            "count": [172, 35, 40, 72],
        }
    )

    return df

df = load_data_cached()

Unless otherwise specified, items appear on the app canvas in the same order they appear in the code. To make the stacked bar chart of nomination outcomes in the top right of our prototype, we attach an Altair to the right_col object we created above.

c = (
    alt.Chart(df)
    .mark_bar()
    .encode(
        x=cohort_var,
        y="count",
        color="last_action",
        order=alt.Order("last_action", sort="ascending"),
        tooltip=[cohort_var, "last_action", "count"],
    )
    .properties(title="Outcome distribution")
)
right_col.altair_chart(c, use_container_width=True)

Amazingly, that’s pretty much it! To launch the app we run the following from a bash prompt:

$ streamlit run my_app_script.py

  You can now view your Streamlit app in your browser.

  Local URL: http://localhost:8501

At this point, we've added the user controls to the sidebar and one plot, and our app looks like this:

Building the Streamlit prototype
Streamlit data app in progress.


The only thing left is to add the other three plots. Be sure to check out the finished, live version here!

Notes

  1. Python 3.8, altair 4.1.0, pandas 1.2.0, streamlit 0.74.1, running in a conda environment.