Streamlit#

  • Maybe the most used freeware dashboard in Python.

  • Enormous choice of elements, widgets and extensions.

  • Can use several interactive graphics packages.

  • Can be run as a local app in a browser or in various other environments.

  • Create a .py file and run using the terminal command below in your favourite Python environment.

    • First time local usage prompts for an email address (press Enter if you do not want to supply it).

    • Code can be updated while the app is running.

    • Any action performed in the app triggers a rerun of the code.

streamlit run /path_to_your_streamlit_folder/appname.py
# Teacher's path to be used below in examples:
path_prefix = "/Users/kristian/Documents/GitHub/IND320/streamlit/"

Examples and documentation#

Basic concepts#

  • Streamlit apps rerun all Python code from top to bottom each time

    • a user interacts with any widget (like buttons, sliders, or text inputs), or

    • when the source code changes.

  • The session state is stored in st.session_state

    • Persistent across interactions.

    • Updated before the code is rerun.

  • Caching of data is important to achieve a responsive app.

  • Progress indicators are useful for large operations.

Static vs dynamic content#

  • Static content in Streamlit means fixed outputs—like Markdown, text, or images that do not change unless the code/script changes.

  • Dynamic content is interactive or data-driven—updates based on user input (widgets, forms), external files, or live data sources.

# static_dynamic.py
import streamlit as st

# Static Example
st.markdown("# Static Dashboard\nThis content won't change until the file is reloaded.")

# Dynamic Example
user_input = st.text_input("Type something:")
if user_input:
    st.write(f"You entered: {user_input}")
file_name = "static_dynamic.py"
# !streamlit run {path_prefix}{file_name}

Magic vs explicit content#

  • Magic: Streamlit automatically displays objects, markdown, and charts on their own line, no function needed (“magic”).

  • Explicit: Using Streamlit functions like st.write, st.markdown, st.dataframe to control display.

# magic_explicit.py
# Magic
"**This shows with magic!**"
import pandas as pd
df = pd.DataFrame({'A': [1,2,3]})
df

# Explicit
st.write("**This shows with explicit command!**")
st.dataframe(df)
file_name = "magic_explicit.py"
# !streamlit run {path_prefix}{file_name}

Session state and on_click / on_event#

  • st.session_state persists variables across reruns, ideal for storing app state and counters.

    • st.session_state is automatically created, and we can extend and manipulate it.

  • on_click/on_change: Assign callback functions to widgets for event-driven updates.

# session_state.py
import streamlit as st

if 'counter' not in st.session_state:
    st.session_state.counter = 0

def increment():
    st.session_state.counter += 1

st.button("Increment", on_click=increment)
st.write(f"Button clicked {st.session_state.counter} times")
file_name = "session_state.py"
# !streamlit run {path_prefix}{file_name}

Reruns and caching#

  • Every interaction reruns the script top-to-bottom; avoid costly computations on every rerun.

  • Use @st.cache_data to store results of expensive operations.

# caching.py
import streamlit as st

@st.cache_data
def get_data():
    import time; time.sleep(2) # Simulate expensive task
    return [1, 2, 3]

st.write("Data:", get_data())

# Interactive part that costs very little compute time
if 'counter' not in st.session_state:
    st.session_state.counter = 0

def increment():
    st.session_state.counter += 1

st.button("Increment", on_click=increment)
st.write(f"Button clicked {st.session_state.counter} times")
file_name = "caching.py"
# !streamlit run {path_prefix}{file_name}

Exercise#

  • Create a streamlit app with a single button and a single output.

  • On first time startup:

    • The button should show the label “Start”.

    • The output should say “Ready”

    • When the button is pressed the first time:

      • A random integer between 1 and 100 is sampled and cached.

      • The button label is changed to “Next”.

      • The output should show the integer.

  • On subsequent button presses:

    • If the integer is even, divide it by 2 and update the output and cache.

    • If the integer is odd, multiply by 3 and add 1, then update the output and cache.

  • The Collatz conjecture says that after clicking the button a sufficient amount of times, the integer will reach 1, no matter what the starting point was.

  • Extra: Let the button say “Half it” and “Triple and add one” based on the current integer shown.

Widgets and interaction#

  • We will look at some basic widgets.

  • The full catalogue is too big for inclusion here.

  • Below:

    • slider (select_slider for a range),

    • selectbox (dropdown menu),

    • pills (toggle elements),

    • radiobuttons (select one alternative),

    • checkbox,

    • toggle (yes/no switch)

  • Also useful: text_input, text_area, date_input, time_input, …

# widgets.py
import streamlit as st

value = st.slider("Pick a value", 0, 100, value=24, step=1)
title = st.selectbox("Select title", 
    ("Ms.", "Mrs.", "Mr.", "Dr.", "Prof.", "Lady", "Lord", "Dutchess", "Duke", "Queen", "King", "Master", "Mistress"))
area = st.pills("Select area", ["Ås", "Ski", "Drøbak", "Kroer", "Vestby"])
subject = st.radio("Choose a subject", ["Deployment", "Data Sources", "Data Quality", "Machine Learning"])
check = st.checkbox("Please, check")
active = st.toggle("Agree, or not?", value=True) 
# Use st.session_state to make printout conditional
st.write("Chosen")
st.write(value, title, area, subject, check, active)
file_name = "widgets.py"
# !streamlit run {path_prefix}{file_name}

Layout#

  • Streamlit apps can be partitioned and layered in various ways.

  • Below:

    • columns (vertical containers),

    • expander (hidden information),

    • tabs (layers of elements on the same page)

import streamlit as st

left, right = st.columns(2, border=True)

with left:
    st.write("This is the left column")
    expander = st.expander("Soo secret")
    expander.write('''
        This is an explanation that is hidden until you click the expander.
    ''')

with right:
    tabA, tabB = st.tabs(["Tab A", "Tab B"])

    tabA.subheader("This is tab A")
    tabA.write("You can put any elements in a tab")

    tabB.subheader("This is tab B")
    tabB.write("You can put any elements in a tab, also here")
file_name = "layout.py"
# !streamlit run {path_prefix}{file_name}

Storing keys and passwords#

  • A streamlit app may be dependent on login credentials or similar to interact with the world.

  • Locally one can store secrets in .streamlit/secrets.toml

    • Exclude these from the GitHub sync using .gitignore!!

  • For apps at streamlit.app, the secrets must be copied into the right place.

import streamlit as st

# Assume secrets.toml has a section called 
# [api] 
# where the secret is stored after 
# key =
st.write("API Key:", st.secrets["api"]["key"])
file_name = "secrets.py"
# !streamlit run {path_prefix}{file_name}

Configuration#

  • An optional file named config.toml can also be put in the .streamlit directory.

  • Some options that can be controlled:

    • the appeareance of the app (theme, colours, element visibility, …),

    • warnings, exceptions and deprecations that will be shown to the users,

    • event logging,

    • automatic reruns on code change,

    • server-side options, etc.

Exercise#

  • Continue on the previous exercise.

  • If the user has pressed the button 20 times without reaching 1, pop up a dialog asking if they really want to continue.