Get started
← Back to all posts

Ultimate guide to the Streamlit library

By Katerina Hynkova

Updated on August 22, 2025

Streamlit is an open-source Python library for building interactive web applications from data scripts in minutes.

Illustrative image for blog post

It enables users to create rich data apps using only Python, without needing HTML, CSS, or JavaScript. Since its initial launch as an open-source project in 2019 (founded by former Google X and Zoox engineers in 2018), the Streamlit library has rapidly evolved with an active community and ecosystem. Streamlit reached version 1.0 in late 2021 and was acquired by Snowflake in 2022, which has further boosted its development and enterprise adoption. As of 2025, Streamlit remains a popular, community-driven project and it’s reportedly used by over 90% of Fortune 50 companies for data apps.

Overview: The Streamlit library’s purpose is to make it effortless to turn data analyses and machine learning models into shareable web tools. It provides a high-level API (streamlit or st module) to define UI elements (text, charts, widgets) in a few lines of Python code. Under the hood, Streamlit apps run a local web server and auto-generate a front-end, so developers can focus on logic and data rather than web infrastructure. The Streamlit ecosystem includes a rich gallery of community components and an active forum. Developers at organizations like Google X and Uber have praised Streamlit for “making it super easy to interact with data” using familiar Python constructs. This ultimate guide covers everything from What is Streamlit in Python? to advanced optimization, serving as the ultimate guide to the Streamlit library for 2024–2025.

What Is Streamlit In Python?

Streamlit is an app framework that turns Python scripts into interactive web applications. Technically, it consists of a lightweight web server and a browser UI. When you run a Streamlit app, your Python script becomes the server-side component, and Streamlit automatically opens a local URL to serve the UI (which is built on React). This client-server architecture means your Python code (server) communicates with the browser (client) to update the interface in real time. Streamlit’s server is built on the Tornado web framework and uses WebSockets to push updates to the browser, maintaining a live two-way connection. In practice, every user interacts with a separate session of your app, and Streamlit reruns the script from the top whenever an interaction (e.g. a button click) occurs. This simple execution model – “recompute on each change” – is key to Streamlit’s design. It trades a bit of computational overhead for simplicity: you write your app as a straight-line script, and Streamlit handles the event loop, state management, and interface rendering for you.

Key components and performance: The Streamlit library exposes an easy API (st methods) for common UI elements. You can use st.write() or st.markdown() to display text, st.button() and st.slider() for inputs, st.line_chart() for quick plots, and so on. Underneath, Streamlit integrates with popular Python libraries – it’s “compatible with basically everything” in the data ecosystem, including Pandas, NumPy, Matplotlib, Plotly, Altair, scikit-learn, PyTorch, and more. Streamlit intelligently renders these objects: for example, calling st.write(my_dataframe) will produce an interactive table, and passing a Matplotlib fig to st.pyplot() will embed the figure in the app. In terms of performance, Streamlit is optimized for rapid development and moderate data sizes. It’s ideal for apps handling up to a few hundred thousand data points or a few simultaneous users on a standard server. Heavy computations can be handled through caching (so they don’t rerun on every interaction) and using efficient libraries (since Streamlit itself is mostly I/O-bound). The use of WebSockets means UI updates are near-instant for users. However, for very large datasets or high-concurrency scenarios, careful optimization is needed (discussed in Advanced Usage). In summary, Streamlit in Python is a library that abstracts away web development, letting you build data apps by writing Python functions and variables – it provides the “Lego blocks” to build whatever you want interactively.

Why do we use the Streamlit library in Python?

Benefits and use cases: The Streamlit library is used because it dramatically simplifies the process of creating data-driven web applications. It addresses the common problem data scientists face: how to share analyses or models with non-technical stakeholders easily. Traditionally, one might build a Flask/Django web app or use Jupyter notebooks for sharing results, but Streamlit offers a much faster and more intuitive path. With Streamlit, you write a Python script as if you were prototyping in a notebook, and with one command you get a web app. This quick development cycle means you can iterate on ideas rapidly – Streamlit automatically refreshes the app on code save, so building a UI is as fast as running a script. The use of Streamlit library leads to high productivity: for example, data scientists have reported going “from coding to deployment in just 2 days” for working apps. Common use cases include data dashboards, machine learning model demos, AI-powered data exploration tools, and internal analytics apps. Streamlit is particularly popular for prototyping machine learning models and visualizations – one user described it as “a great way to share machine learning models and analyses” with others. Instead of spending time on web UI details, you can focus on data logic, which is why Streamlit is beloved in the data science community.

Industry Adoption and Comparison: Streamlit’s efficiency has driven broad adoption. It is used by students, researchers, and at companies of all sizes – in fact, by late 2024 it was being used in over 90% of Fortune 50 companies, indicating how quickly it became a standard tool. Many tech teams (e.g. at Google X, Stitch Fix, Uber, etc.) publicly praised Streamlit for bridging the gap between complex analysis and accessible tools. Compared to alternatives, the Streamlit library in Python emphasizes simplicity and immediacy. For example, Plotly Dash (another Python dashboard framework) offers more fine-grained control but requires learning a callback structure and has a steeper learning curve. Jupyter notebooks are great for exploration but not for interactive apps or polished sharing – as one user put it, Streamlit “does right everything Jupyter notebooks got wrong” in terms of building shareable apps. Streamlit does have some limitations (discussed later), but for simpler, data-oriented apps the speed of development is unbeatable. In short, we use the Streamlit library in Python because it allows us to go from idea to deployed app extremely quickly, solving the “last mile” problem of data projects. It democratizes app creation (hence “Streamlit democratizes building data apps”) so that anyone familiar with Python can deliver interactive tools without specialized web programming knowledge.

Getting started with Streamlit

Getting started with the Streamlit library is straightforward. In this section we’ll cover installation and setup on various platforms, and walk through your first Streamlit app step-by-step. By the end, you’ll have a running web app and knowledge of common beginner pitfalls. Let’s dive in!

Installation and setup

You can install Streamlit like any other Python library. The primary methods are using pip or conda, and Streamlit supports Windows, macOS, and Linux equally. Below we provide instructions for different environments and tools:

  • Using pip (Python’s package installer): If you have Python installed, the simplest way is pip install streamlit. This downloads the Streamlit library from PyPI. For example, open a terminal (Command Prompt on Windows or Terminal on Mac/Linux) and run: pip install streamlit. Once done, you can verify by checking the version: streamlit --version. You should see a version like Streamlit 1.xx.x. (If pip refers to Python 2 on your system, use pip3 install streamlit for Python 3.) The pip installation is the most up-to-date source (Streamlit’s own docs recommend “Just install it like any other Python library”). After installing, you can test with the streamlit hello command to run an example app.

  • Using Anaconda/conda: If you prefer Anaconda or Miniconda, you can install the Streamlit library via conda. The Streamlit package is available on the conda-forge channel. Open the Anaconda Prompt and create or activate a Python environment (e.g. conda create -n streamlit-env python=3.10 && conda activate streamlit-env). Then run: conda install -c conda-forge streamlit. This will fetch Streamlit and all dependencies from conda-forge. Using conda is useful on some systems (e.g. M1 Macs or Linux) because it can handle binary dependencies smoothly. Note that sometimes the conda package may lag behind the pip version; if you need the very latest features, pip might be preferable. After conda installation, run streamlit hello or streamlit run your_app.py to ensure it’s working.

  • In Visual Studio Code (VS Code): VS Code is a popular editor for Python. To install the Streamlit library in VS Code, you don’t need special steps beyond the above – install via pip or conda in the terminal integrated in VS Code. For example, open a VS Code terminal for your project’s virtual environment and run pip install streamlit. VS Code will then recognize the streamlit module in IntelliSense. A tip: you can add a VS Code debug configuration to run Streamlit (by launching streamlit run as the program) if you want to debug your app. But initially, simply use the terminal: after installing, run streamlit run your_script.py from VS Code’s terminal to launch the app. Ensure you have Python selected as the interpreter in VS Code so it uses the correct environment where Streamlit is installed.

  • In PyCharm: To install Streamlit in PyCharm, you can use PyCharm’s package management or a terminal. In PyCharm, open Settings -> Python Interpreter -> + (Add Package) and search for “streamlit”. Install the latest version from there. Alternatively, use the terminal within PyCharm and run pip install streamlit (it will install into the project’s interpreter). After installation, you can create a Run/Debug configuration for Streamlit: set the script to streamlit and parameters to run app.py. PyCharm will then run Streamlit and you can see logs in the console. PyCharm on Windows may need you to start the app through streamlit run rather than using the green run button on a Python file (which would just run it as a normal script). Overall, ensure the interpreter has Streamlit, then run the app via the command or a custom configuration.

  • Using Anaconda navigator (GUI): If you prefer not to use the command line, Anaconda Navigator provides a GUI to manage packages. To install Streamlit via Navigator, open Environments, select your environment (or create one), then use the search bar to find “streamlit”. Make sure to select All Channels or conda-forge in the package search. You should see Streamlit; check the box and apply to install. This will perform the same as the conda command above. Note that you might need to enable conda-forge channel in Navigator’s settings if Streamlit isn’t found by default. After installation, you can launch a terminal from Navigator and run streamlit hello to test, or open Jupyter Notebook/VS Code from Navigator in that environment.

  • Windows, Mac, and Linux specifics: The installation process is virtually identical on different operating systems, with minor differences in how you access the terminal. On Windows, use Command Prompt or PowerShell (ensure Python and pip are on your PATH). On macOS and Linux, use the Terminal. One common Windows issue is if the streamlit command isn’t recognized after install – this might happen if the Scripts directory wasn’t added to PATH. In that case, try python -m streamlit run app.py as an alternative to ensure you’re invoking the correct Python. Mac users using Homebrew Python might need to use pip3. Linux users should ensure pip is updated and may need to install system packages for some Streamlit dependencies (though pip/conda usually handle it). In summary, “pip install streamlit” or the conda equivalent works on all platforms. The Streamlit library supports Python 3.8 through 3.12 (at time of writing), so ensure your Python version is up to date.

  • Docker: Streamlit apps can be containerized for deployment. You can use Docker by writing a simple Dockerfile that installs Streamlit and runs your app. For example, a basic Dockerfile might use the official Python image:

    FROM python:3.10-slim
    WORKDIR /app
    COPY requirements.txt ./
    RUN pip install -r requirements.txt
    COPY . .
    EXPOSE 8501
    CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]

    In your requirements.txt, include streamlit (and any other libs). Build the image with docker build -t myapp . and run with docker run -p 8501:8501 myapp. Docker will handle installation inside the container. Streamlit’s documentation provides a detailed guide on using Docker. This approach is great for cloud deployment or ensuring reproducibility. Just remember to set --server.address=0.0.0.0 so Streamlit listens on all interfaces in the container.

  • Virtual environments: It’s recommended to use a virtual environment to manage your Python packages. Streamlit works well in venv, virtualenv, or pipenv. For example, to use a venv:

    python3 -m venv venv
    source venv/bin/activate  # (on Windows: venv\Scripts\activate)  
    pip install streamlit

    This keeps the Streamlit library and its dependencies isolated from your system Python. If using pipenv, run pipenv install streamlit. Using virtual environments avoids version conflicts (especially since Streamlit has specific version ranges for some dependencies). Make sure to activate the environment before running the app.

  • Cloud setup (generic): To run Streamlit in the cloud, you typically set up a VM or use a PaaS. On a cloud VM (AWS EC2, Azure VM, GCP Compute Engine, etc.), the steps are the same: install Python, pip/conda, then install Streamlit and run your app. Make sure to allow the app’s port (default 8501) through any cloud firewalls or security groups. Streamlit also offers Community Cloud (a managed hosting) where you can deploy apps directly via GitHub – however, focusing on local development, you might use Community Cloud later for sharing. Another cloud option is to deploy via containers (using the Docker image built earlier) on services like AWS ECS or Heroku. The key for cloud deployment is to set streamlit run app.py --server.port=$PORT --server.address=0.0.0.0 (where $PORT is often provided by the platform). For most beginners, the first step is running locally; deployment to cloud can be done once the app is stable.

  • Troubleshooting installation issues: Common installation problems include missing dependencies or network issues. If pip install streamlit fails with a compiler error, make sure you have the latest pip and wheel (pip install --upgrade pip wheel). Streamlit’s core is pure Python, but it uses packages like numpy, pandas, etc., which have pre-built wheels – if those fail to install, check that your environment can install those (for example, on Linux you may need python3-dev and build tools). If you see “No module named streamlit” when running, it means the install didn’t go through or you’re using a different Python environment – double-check that pip is pointing to the correct Python. On Windows, if the streamlit command isn’t found, try closing and reopening the terminal or add the Python Scripts folder to your PATH. Another tip: run python -m streamlit hello to bypass shell resolution issues. If using conda and it hangs or fails to solve environment, consider using -c conda-forge (as shown above) or just use pip inside the environment. Most installation issues can be resolved by ensuring you have a clean environment and using the latest installer commands. The Streamlit community forum is also full of Q&A for installation hiccups.

Once Streamlit is installed, you’re ready to create your first app!

First example: your first Streamlit app

Let’s walk through a complete Streamlit app example step by step. This simple app will take some user inputs (principal, interest rate, years), perform a calculation (future value of an investment), and display the results with a chart. It will demonstrate basic Streamlit usage: displaying text, widgets for input, and outputting results. Follow along by creating a file first_app.py and adding the code below:

import streamlit as st
import pandas as pd
import numpy as np

st.title("Loan Growth Calculator")
st.write("This app calculates how an investment grows over time with interest.")

# User inputs
principal = st.number_input("Initial principal ($):", min_value=0.0, value=1000.0)
rate = st.number_input("Annual interest rate (%):", min_value=0.0, value=5.0)
years = st.slider("Duration (years):", 1, 30, 10)
compound = st.selectbox("Compounding frequency:", ["Annual", "Semi-Annual", "Quarterly", "Monthly"])

# Compute compounding periods per year
freq_map = {"Annual": 1, "Semi-Annual": 2, "Quarterly": 4, "Monthly": 12}
n = freq_map[compound]

# Calculation with error handling try:
r = rate / 100.0 # convert percentage to decimal
final_amount = principal * ((1 + r/n) ** (n * years))
st.metric("Final Amount", f"$ {final_amount:,.2f}")
 # Prepare data for yearly balance chart
timeline = np.arange(0, years + 1)
balances = principal * ((1 + r/n) ** (n * timeline))
df = pd.DataFrame({"Year": timeline, "Balance": balances})
st.line_chart(df.set_index("Year"))
except Exception as e:
st.error(f"An error occurred: {e}")

Let’s break down what this code does, line by line:

  1. Imports: The code imports streamlit as st and also pandas and numpy for calculations. Streamlit works with data libraries like pandas and numpy seamlessly, so we import them to help compute the results.

  2. Title and description: st.title("Loan Growth Calculator") sets a big title in the app. We then use st.write() to display a description. This creates a friendly header and explanation for the user at the top of the app.

  3. User input widgets: We create several input widgets:

    • st.number_input("Initial principal ($):", ...) provides a numeric input box for the principal amount. We set a minimum of 0.0 and a default value of 1000.0.

    • st.number_input("Annual interest rate (%):", ...) for the interest rate, default 5.0 (%).

    • st.slider("Duration (years):", 1, 30, 10) gives a slider from 1 to 30 years, defaulting to 10.

    • st.selectbox("Compounding frequency:", ["Annual", "Semi-Annual", ...]) lets the user pick how often interest is compounded per year.

      Each of these calls immediately draws the widget in the UI and returns the current value (we store them in variables principal, rate, years, compound). Streamlit automatically remembers widget states on each run, so when the user adjusts something, the script reruns and these variables get the new values.

  4. Computation: We define a freq_map to translate the compounding frequency into a number of periods (Annual -> 1, Semi-Annual -> 2, etc.), then get n = freq_map[compound]. Then, inside a try block, we perform the future value calculation:

    • We convert the percentage rate to a decimal (r = rate / 100.0).

    • Compute final_amount = principal * ((1 + r/n) ** (n * years)). This is the formula for compound interest.

    • We use st.metric to display the final amount as a nice big number, labeling it "Final Amount" and formatting as currency. The st.metric widget is great for highlighting key numbers (it even can show deltas, though we don’t use that here).

    • Next, we prepare data for a chart showing the balance over each year. We create a NumPy array timeline from 0 to years, and calculate balances for each year (compounding annually for simplicity in the chart). We put this into a pandas DataFrame with columns Year and Balance.

    • st.line_chart(df.set_index("Year")) plots the balance over time. Streamlit knows how to plot a DataFrame – by setting Year as index, the x-axis becomes years and y-axis is Balance. The chart will update whenever inputs change.

    • All of this is in a try/except so that if anything goes wrong (e.g. a zero division or an unexpected input), we catch the exception and display an error message with st.error().

  5. Error handling: In the except Exception as e block, we use st.error() to display a red error box with the message. For instance, if years were 0 (though our slider prevents that) or any other issue occurred, the user would see a friendly error instead of the app crashing. This pattern of wrapping calculations in try/except is good practice in Streamlit apps to handle edge cases.

Expected output: When you run streamlit run first_app.py, Streamlit will open a local browser window (usually at http://localhost:8501) to display the app. At the top, you’ll see “Loan Growth Calculator” as a header. The input widgets (principal, interest rate, years, frequency) will appear in the main panel (Streamlit places them in order). By default, principal $1000, rate 5%, 10 years, monthly compounding will be selected. The app will calculate the final amount – about $1,647.01 in this case – and display it as a Final Amount metric. Below that, a line chart appears, plotting the balance each year from Year 0 ($1000) up to Year 10 (~$1647). If you adjust inputs (say move the Duration slider to 20 years or change interest to 7%), the app will rerun and update the number and chart instantaneously. You can play with the Compounding frequency dropdown to see how Annual vs Monthly compounding slightly changes the final value. All of this happens without a page refresh – Streamlit handles updating the components dynamically.

Beginner mistakes to avoid: When creating your first Streamlit app, keep these tips in mind:

  • Run the app with Streamlit: Don’t try to execute the script with python first_app.py. Instead use the Streamlit CLI: streamlit run first_app.py. This is a common mistake for first-timers – the app interface only appears when run through the Streamlit command.

  • Use Streamlit functions for output: If you use print() or matplotlib.pyplot.show() in your script, you won’t see those in the webpage. Always use st.write, st.pyplot, st.dataframe, etc., to display content in the app. For example, in Streamlit, st.write(df) will show a DataFrame, whereas print(df) prints to the console (not visible to the user).

  • Import and namespace: Make sure to import streamlit as st and use the st. prefix for all Streamlit calls. Forgetting the st. or failing to import Streamlit will lead to NameError or your app doing nothing. In our code, every Streamlit function is prefixed with st.

  • Understand reruns and state: Streamlit reruns your script from top to bottom on each interaction. If you assign a variable outside any widget logic, it will reset each time. For instance, if you create a counter count += 1 in your code, it will reset on rerun unless you use session state (covered later). Beginners sometimes expect Streamlit apps to behave like single-run scripts, but remember the reactive rerun model. Embrace it and use session state for values that should persist (like form inputs or counters).

  • File paths: If your app reads a local file (e.g. an image or CSV), use absolute paths or __file__ relative paths. The working directory in Streamlit is the location of your script by default, which helps. In our example, we assume an example.jpg in the same folder if we were to use an image. If you see “File not found” errors, check the path relative to your script.

With these points in mind, you’ve created a working Streamlit app! Now let’s explore Streamlit’s core features in detail.

Core features

Streamlit provides a host of high-level features to make building apps easy. In this section, we’ll cover five core aspects of the Streamlit library, each with examples, usage syntax, integration notes, and common pitfalls:

  1. Displaying text and media – writing text (markdown, code, LaTeX) and displaying images, audio, etc.

  2. Displaying data and charts – showing tables, data frames, metrics, and creating plots.

  3. Interactive widgets – adding inputs like buttons, sliders, file uploaders, and reacting to them.

  4. Layout and containers – organizing your app with sidebars, columns, tabs, and expanders.

  5. State and callbacks – managing user state with session state and forms, and optimizing with caching.

Let’s go through each feature one by one.

Displaying text and media

What it does: Streamlit makes it simple to display text in various formats, as well as media like images, audio, and videos. This core feature is about presenting information to the user. You can use it for titles, formatted documentation, code snippets, mathematical formulas, and multimedia content.

Syntax and key functions: The Streamlit library offers several functions for text:

  • st.title(), st.header(), st.subheader() – for headings of different sizes.

  • st.write() – a versatile command that writes arguments in an intelligent way (text, data, plots, etc.). For strings, it’s similar to print but outputs to the app.

  • st.text() – displays preformatted text (monospace, no formatting).

  • st.markdown() – renders a string as Markdown (supports bold, italics, code, lists, etc.). You can also enable HTML in markdown via unsafe_allow_html=True (for advanced use).

  • st.latex() – displays a LaTeX formula nicely (use raw string for LaTeX).

  • st.code() – displays a code block with optional language syntax highlighting.

  • st.metric() – (though more of a data display) shows a big number with a label.

    For media:

  • st.image() – displays images (from file, URL, or PIL image). Supports JPG, PNG, etc. and has parameters like width, caption.

  • st.audio() – plays audio files or streams (MP3, WAV…).

  • st.video() – embeds video files or YouTube links.

Examples: Below are examples of displaying text and media in an app.

Text output example: This snippet shows various text elements:

st.title("Streamlit Text Demo")
st.header("Displaying text:")
st.text("Plain text displayed in a fixed-width font.")
st.markdown("Streamlit **supports Markdown**, _including formatting_ and `code`.")
st.latex(r"E = m c^2")  # display a LaTeX formula
code_str = """
def hello(name):
print(f"Hello, {name}")
"""
st.code(code_str, language="python")

When run, this will produce a title “Streamlit Text Demo” and a header “Displaying text:”. Below that:

  • A plain text line in a fixed-width font (useful for unformatted output or logs).

  • A Markdown line where bold and italics and code are rendered appropriately (Streamlit’s markdown supports GitHub-flavored Markdown).

  • A beautifully rendered formula for Einstein’s energy-mass relation E = mc².

  • A syntax-highlighted code block showing a Python function (thanks to st.code(..., language="python")).

These make it easy to present information. st.write() could achieve many of the above as well (it auto-detects types), but using specific functions gives you more control.

Media output example: Next, let’s display an image and audio:

from PIL import Image
try:
img = Image.open("sample_photo.jpg")
st.image(img, caption="Sample image", use_column_width=True)
except FileNotFoundError:
st.error("Image file not found. Please check the path.")
# Play an audio file from a URL (or local file)
st.audio("https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3")

In this code, we use PIL to open an image file. If the file exists, st.image will show it (with a caption and scaled to column width). If not, we catch the FileNotFoundError and display an error message to the user via st.error. This pattern ensures the app doesn’t crash if the image is missing – instead the user sees a friendly error box. Then st.audio is used to embed an audio player. We provided a sample MP3 URL; in an actual app, you could use a local file (e.g. an uploaded file or a static resource). Similarly, st.video("https://youtu.be/your_video") can embed a YouTube video by URL, or st.video('video.mp4') to play a local video file. These multimedia functions make it easy to add rich content (like charts, sound alerts, or explanatory videos) to your app.

Performance and integration: Text and media elements are lightweight for Streamlit to render. You can safely display large Markdown documents or numerous images. However, be mindful of image sizes – sending a 10MB image to every user will affect load times. Streamlit doesn’t heavily compress images, so you might want to resize images for web. For text, using st.markdown with extensive HTML/CSS via unsafe_allow_html=True is possible but not officially recommended (it’s a hack to inject custom styling/JS and can break if Streamlit’s DOM changes). Instead, stick to Markdown and Streamlit’s theming for consistency. Integration-wise, Streamlit can display objects from many libraries directly: for example, you can pass a Plotly figure to st.plotly_chart (or even to st.write and Streamlit will know how to render it). The “magic” of st.write is that it uses Display logic similar to Jupyter. This extends to text: st.write("### Hello") would interpret the string as Markdown and render a header. But using st.markdown("### Hello") explicitly might be clearer. In summary, for text and media, use the appropriate st functions to ensure proper formatting. There are few performance issues here, except if you try to display extremely large text (thousands of lines) which could slow the browser; in such cases, consider using st.file_uploader to let user download logs or content rather than dumping in the UI.

Common errors/pitfalls: A common mistake is forgetting to set unsafe_allow_html=True when trying to insert raw HTML. By default, Streamlit sanitizes Markdown to prevent malicious HTML/JS. If you do need to embed custom HTML (say, an iframe or custom styling), use st.markdown(html_string, unsafe_allow_html=True). Another pitfall is using backslashes in string for LaTeX – remember to use raw strings r"latex here" or escape backslashes properly. If your text isn’t appearing, check that the code is actually being executed (e.g. within an if that isn’t running). For images, ensure the path is correct or use st.image(url) for remote images. Finally, note that st.title and st.header also accept Markdown in their string (except they automatically render it bold larger). If you see literal markdown syntax in your output, it means you used st.text or st.write on a string that wasn’t auto-interpreted – using st.markdown would fix it.

Displaying data and charts

What it does: A core purpose of Streamlit is to display data – whether in tabular form or visualizations. Streamlit provides convenient functions to show data frames, tables, metrics, and charts. It also integrates with Python’s visualization libraries (Matplotlib, Plotly, Altair, etc.) so you can embed those charts in your app. This feature lets users explore data dynamically.

Syntax and key functions: Key Streamlit functions for data display include:

  • st.dataframe(data) – displays a dataframe (Pandas DataFrame, PySpark DF, etc.) as an interactive table with sorting and scrolling. It’s like an Excel-view of your data.

  • st.table(data) – displays a static table (not interactive, good for small tables or summary statistics).

  • st.json(obj) – nicely prints a JSON or dictionary object with indentation.

  • st.metric(label, value, delta=None) – shows a big number with an optional delta (change) indicator.

  • st.line_chart(data), st.area_chart(data), st.bar_chart(data) – quick ways to plot data (Streamlit will use matplotlib or Vega-Lite under the hood). Just pass a DataFrame or array and it plots automatically.

  • st.pyplot(fig) – displays a Matplotlib figure.

  • st.plotly_chart(fig) – displays a Plotly figure.

  • st.altair_chart(chart), st.vega_lite_chart(data, spec), etc. – functions for other library charts (Altair, Bokeh, etc.).

  • st.map(df) – quickly plots latitude/longitude points on a map (uses PyDeck/Mapbox under the hood for a basic map).

Examples: Let’s demonstrate with examples.

Tabular data and built-in chart example:

import pandas as pd
import numpy as np

# Create a sample DataFrame
data = np.random.randn(5, 3)
cols = ['A', 'B', 'C']
df = pd.DataFrame(data, columns=cols)

st.write("DataFrame:", df)  # Streamlit will render this as a table
st.dataframe(df.style.highlight_max(axis=0))  # Interactive dataframe with highlighting # Display a summary metric
avg_a = df['A'].mean()
st.metric(label="Average of A", value=f"{avg_a:.2f}")

# Plot a bar chart of the data
st.bar_chart(df)

Here, we first use st.write("DataFrame:", df). Streamlit sees that df is a pandas DataFrame and automatically renders it as a table (with the text "DataFrame:" above it). This is a quick way to display data. Next, st.dataframe(df.style.highlight_max(axis=0)) shows the same DataFrame but with some styling – we used pandas Styler to highlight the max in each column. st.dataframe can take a pandas Styler or plain DataFrame. The displayed table allows sorting by columns and horizontal scrolling if wide. It’s great for exploratory apps. We then use st.metric to show a single number: the average of column A, formatted to two decimals. The metric will display as “Average of A – 0.xx”. (If we had a delta, e.g. delta="+0.5", it would show an arrow indicator). Finally, st.bar_chart(df) plots the DataFrame as a bar chart. Since df has 5 rows and 3 columns, Streamlit will create a grouped bar chart with x-axis as index (0 to 4) and bars for A, B, C. These st._chart functions are a quick way to visualize data without writing plotting code. They infer chart type and axes from the data shape.

Integration with external plotting library example: Streamlit doesn’t lock you into its own charts – you can use libraries like Plotly, Altair, or Matplotlib seamlessly. For instance, using Plotly:

import plotly.express as px

df2 = px.data.iris()  # load sample dataset
fig = px.scatter(df2, x="sepal_width", y="sepal_length", color="species",
title="Iris Sepal Dimensions")
st.plotly_chart(fig, use_container_width=True)

In this code, we load Plotly’s built-in Iris dataset and create an interactive scatter plot by species. st.plotly_chart(fig) renders this Plotly figure in the Streamlit app with full interactivity (hover tooltips, legend filtering, etc.). The use_container_width=True ensures the chart scales to the app’s width. Similarly, st.pyplot(fig) would render a Matplotlib figure (make sure to call plt.close() if generating new figs in loops to avoid duplicate outputs), and st.altair_chart() works with Altair Chart objects. This tight integration means you can leverage the extensive visualization ecosystem of Python in your Streamlit app. In fact, you can even mix them: perhaps use Plotly for one chart and Streamlit’s st.line_chart for a quick preview elsewhere.

Using st.map for geodata: As a quick example, if you have latitude-longitude data, Streamlit can map it:

locations = pd.DataFrame({
 'lat': [40.7128, 34.0522, 37.7749],
 'lon': [-74.0060, -118.2437, -122.4194]
})
st.map(locations)

This would show an interactive map with points at New York, Los Angeles, and San Francisco. It’s a very handy one-liner for geospatial visualization (uses kepler.gl/PyDeck under the hood). For more complex maps, you could use st.pydeck_chart with a PyDeck config.

Performance and integration: When displaying data, consider the size. st.dataframe can handle reasonably large data (thousands of rows) by virtualizing (only rendering what’s visible). However, very large tables (e.g. 100k+ rows) might be slow in the browser. If you have big data, prefer summary or sampling, or provide a download link for the full dataset. Charts performance depends on the library – built-in st.line_chart and others use Vega-Lite which can typically handle a few thousand points smoothly. Plotly and Altair also handle moderate data sizes well; beyond that, consider downsampling or aggregation for performance. Another integration note: Pandas Styler support – as shown, you can pass a styled DataFrame to st.dataframe to get conditional formatting (this feature arrived in Streamlit 1.10+). Also, Streamlit introduced st.data_editor (formerly experimental) to allow editable tables, which is another advanced feature for two-way interaction (not covered in depth here, but it lets users edit cell values and returns the edited DataFrame).

Common errors/pitfalls: A common confusion is that st.write(df) and st.dataframe(df) are similar but not identical – st.dataframe allows interactive features and can be styled. st.table is static (no scrollbars), so use it only for small tables. If your data doesn’t display, ensure it’s in a supported format (e.g., a pandas DataFrame or list of lists). If you see “dataframe is too large to display” warnings, that’s Streamlit telling you to reduce data or slice it. For charts, a typical mistake is forgetting to set an index or the data shape not matching expectations. For example, if you do st.line_chart(np.random.randn(10)), it will assume the array is one time series of length 10. If it’s 2D data, make sure it’s in a shape Streamlit can interpret (DataFrame or 2D array). Another thing: if using Matplotlib, always create a new figure (e.g. with plt.subplots() or plt.figure()) inside the part of code that runs each time, otherwise you might reuse old figures inadvertently. And remember to call st.pyplot(fig) after drawing on fig (or simply call plt.plot(...) then st.pyplot(plt) which grabs current figure). For Plotly, ensure you pass a figure object, not just a data structure. Lastly, when using st.map, the DataFrame must have columns lat and lon (or latitude and longitude). If you pass a DataFrame with those column names, it just works – if you use different names, st.map will not know how to plot it (and likely show nothing). In such cases, rename or use st.pydeck_chart for a custom map.

Interactive widgets

What it does: Interactive widgets are the heart of Streamlit’s interactivity. These are UI controls like buttons, sliders, text inputs, checkboxes, date pickers, file uploaders, etc., that allow users to input data or trigger actions in your app. When a user interacts with a widget, Streamlit captures the input and triggers the app script to rerun, updating outputs based on the new inputs. This makes apps dynamic – for instance, changing a slider can recompute a plot, clicking a button can reveal results, etc.

Syntax and key functions: Streamlit offers a rich set of widgets:

  • Buttons and toggles: st.button(label) creates a button. st.checkbox(label) creates a checkbox (returns True/False). st.radio(label, options) for selecting one of a small set of options via radio buttons.

  • Select and multiselect: st.selectbox(label, options) allows selecting one option from a dropdown. st.multiselect(label, options) allows multiple selection (returns a list of choices).

  • Sliders: st.slider(label, min, max, value) for numeric ranges, including int/float ranges or date ranges. Also st.select_slider() for non-numeric discrete slider.

  • Text inputs: st.text_input(label) for one-line text. st.text_area(label) for multi-line text. Both have options like value (default text) and type="password" for hidden input.

  • Number inputs: st.number_input(label) for integers or floats with bounds.

  • Date/time inputs: st.date_input(label) and st.time_input(label) to pick dates and times.

  • File uploader: st.file_uploader(label, type=['.csv', ...]) lets user upload a file. Returns a file-like object (or None if no file yet).

  • Camera input: st.camera_input(label) (recently introduced) to capture an image from the user’s webcam.

  • Color picker: st.color_picker(label) to choose a color.

  • Forms and submit: (We’ll touch in the State section) – st.form allows grouping widgets with a single submit button.

  • Each widget typically returns a value: e.g., value = st.slider(...) gives the current slider value. One exception is st.button – it returns True only on the run when it was clicked, otherwise False.

Using the widgets: To handle widgets, you usually write code like:

option = st.selectbox("Choose an option:", ["A", "B", "C"])
st.write("You selected:", option)

This will display a dropdown and below it echo the choice. On each interaction, option gets updated and st.write outputs the new selection.

Examples: Let’s illustrate with some common widgets.

Basic input widgets example:

name = st.text_input("Enter your name:")
age = st.number_input("Your age:", min_value=0, max_value=120, value=25)
hobby = st.selectbox("Favorite Hobby:", ["Reading", "Sports", "Music", "Travel"])
agree = st.checkbox("I agree to the terms and conditions")

if st.button("Submit"):
 if not name:
st.error("Please enter a name before submitting!")
 else:
st.write(f"Hello **{name}**, age {age}.")
st.write(f"Your favorite hobby is **{hobby}**.")
st.write("Terms accepted:", "✅" if agree else "❌")

In this snippet:

  • We have a text_input for the name. If the user leaves it blank and hits submit, we’ll show an error.

  • A number_input for age with a range 0–120 and default 25.

  • A selectbox for hobbies with some options.

  • A checkbox for agreeing to terms.

These widgets are rendered immediately as the script executes top to bottom. Initially, name will be "" (empty), age 25, hobby "Reading" (the first option), and agree False. Nothing is printed yet except the widgets. If the user fills in "Alice", picks age 30, selects "Music", and checks the box, those variables update. The Submit button is special: it doesn’t return a persistent value, but when clicked it causes the script to rerun and st.button("Submit") will yield True only on that run. We wrap our processing logic in if st.button("Submit"): so it runs only after the user clicks. Inside, we validate that name is not empty – if it is empty, we use st.error to show a message. If not, we greet the user and display their choices. The checkbox’s boolean agree is used to show a checkmark or cross (using emoji) for whether terms were accepted. This demonstrates how clicking a button triggers a response. The pattern is: check button, gather widget values, then act accordingly.

File uploader and camera example:

uploaded_file = st.file_uploader("Upload a CSV file", type=["csv"])
if uploaded_file:
 try:
data = pd.read_csv(uploaded_file)
st.write(f"Uploaded data has {data.shape[0]} rows and {data.shape[1]} columns.")
st.dataframe(data.head())
 except Exception as e:
st.error(f"Error reading file: {e}")

photo = st.camera_input("Take a photo")
if photo:
st.image(photo, caption="Your photo")

In this code:

  • st.file_uploader allows a user to choose a file from their computer. We restricted it to CSV files for this example. Initially, uploaded_file is None (Falsey). Once the user selects a file, uploaded_file becomes a file-like object (you can get its name, read it, etc.). We then attempt to read it with pandas. If successful, we display some info about the DataFrame and show the first few rows in an interactive table. If the CSV is malformed or pd.read_csv fails, we catch the exception and display an error with st.error. This shows how to handle user-provided input robustly.

  • st.camera_input activates the user’s webcam and places a “Take Photo” button. When the user takes a photo, photo becomes an UploadedFile object (similar to file_uploader). We can treat it like an image file – e.g., directly pass it to st.image. In this example, once a photo is captured, we display it with a caption. This widget is handy for apps that involve image processing or user identification.

How Streamlit handles widget interactions: It’s important to realize that any change in a widget’s value causes the entire script to rerun. Streamlit keeps track of widget states so that after rerun, the widgets don’t reset to defaults but retain what the user had set. In the above examples, if you type a name or upload a file, the script reruns, but name or uploaded_file still hold the values after rerun. This magic is done via a combination of session state and widget IDs behind the scenes.

Performance considerations: Widgets themselves are lightweight. The main consideration is to avoid expensive computations on every rerun, especially if those computations aren’t affected by the widget that changed. We’ll discuss caching in the advanced section, but a quick tip: if you have a heavy initialization (like loading a big dataset), do it before widget definitions or use st.cache_data so that a widget change doesn’t recompute everything from scratch. Another aspect is debouncing: Some widgets like sliders will, by default, cause many reruns as you slide. Streamlit mitigates this by only updating on release for sliders by default. Text input by default updates on every keystroke; you can set st.text_input("...", on_change=some_callback) or use st.form to have more control (submitting a batch of changes together).

Common errors/pitfalls: A frequent question is “How do I run some code only when a button is clicked?” – the pattern above addresses it: check if st.button(...): and put code inside. If you put code below a button without that if, it will run every time the script runs, which may not be what you want. Another pitfall: forgetting that widget interactions reset variables not stored in Streamlit’s state. For example, if you have a counter count = 0 at the top of your script and later in the script you do if st.button("++"): count += 1; st.write(count), you might expect it to increment. But because the script reruns from scratch on each click, count will reset to 0 at the top each time, and the button click will result in 1 every time. The fix is to use st.session_state to persist count (we cover this in the next section). This is a common stumbling block for newcomers – understanding that normal Python variables don’t persist across runs (except in closure scopes or cached functions). Another pitfall is using mutable defaults for widgets. For instance, multiselect returning a list – if you store it and modify that list, on next rerun it might behave oddly; it’s better to treat widget return values as snapshots and not mutate them (or if you do, manage it in session state). Also note, all widget keys must be unique. If you put two widgets with the same label or explicitly same key, Streamlit will raise an error about duplicate widget key. You can fix it by providing unique key="something" arguments to each, if you have repeating labels in loops or dynamic forms.

In summary, interactive widgets in Streamlit are straightforward to use: just call the st.xyz_widget() function and use the returned value in your app logic. They handle rendering and state automatically, enabling a reactive programming style where your UI and code state are synced.

Layout and containers

What it does: Streamlit’s layout system allows you to arrange your app components in various ways. By default, elements are stacked vertically in the order you call them. However, you can use containers (columns, expandable sections, sidebars, tabs) to create multi-column layouts, group content, or hide/show content. This feature is about improving the organization and user experience of your app.

Key layout elements:

  • Sidebar: st.sidebar is a special container that renders on the left side as a vertical sidebar. You can use it like with st.sidebar: ... to add widgets or text to the sidebar. Common use: put input controls in the sidebar to free up main space for outputs.

  • Columns: st.columns(n) creates n equal-width columns and returns column objects. You can also specify unequal widths like st.columns([2,1]). Use with colX: block to place elements in a column.

  • Tabs: st.tabs(list_of_names) creates a tabbed interface (like browser tabs). It returns tab objects to use in a with context. Each tab’s content is only visible when that tab is selected.

  • Expander: st.expander(label) creates an expandable/collapsible section. Content inside is hidden by default and shown when the user clicks to expand.

  • Empty/Container: st.container() creates a generic container you can fill later (useful for dynamic content). st.empty() reserves a space that can be updated (e.g., using .write() or .chart() on the returned placeholder object).

  • Page config: Although not a container, st.set_page_config(layout="wide") can be used at the top of your script to enable a wide layout (so content can span more horizontally).

  • Vertical spacing & columns spacing: Minor but st.write("") or st.markdown("<br>", unsafe_allow_html=True) can add vertical gaps. There’s also st.caption for small text.

Examples:

Using the sidebar and columns:

# Sidebar content with st.sidebar:
st.title("App Options")
user_choice = st.radio("Choose view:", ["Summary", "Detailed"])
color = st.color_picker("Pick a theme color", "#00f900")

st.write(f"Sidebar selection: **{user_choice}**")  # reflect the choice in main area # Columns layout
col1, col2 = st.columns(2)
with col1:
st.subheader("Column 1")
st.write("This is content in the left column.")
with col2:
st.subheader("Column 2")
st.write("This is content in the right column.")

# You can also directly use columns without 'with'
col1.metric("Temperature", "25 °C")
col2.metric("Humidity", "60 %")

In this example:

  • We open a with st.sidebar context. Any Streamlit calls inside will appear in the app’s sidebar (a narrow panel on the left with a grey background by default). We put a title and two widgets there. So the sidebar will show “App Options” as a header, a radio button group for “Summary” or “Detailed”, and a color picker. Typically, one might use the radio to switch app modes and the color to set a theme or plot color.

  • In the main layout, we use the user’s sidebar selection just by printing it (to show how to access sidebar values). st.sidebar.radio actually returns the selection just like st.radio would, we stored it in user_choice.

  • Next, we create two columns of equal width: col1, col2 = st.columns(2). We then use with col1: and with col2: to direct content into each column. We put subheaders and text in each. Visually, these will appear side by side in the app, allowing a two-column layout on the page.

  • After the with blocks, we demonstrate that you can also call methods on column objects directly without using with. For example, col1.metric(...) places a metric in column 1, and col2.metric(...) in column 2 on the same row. This will display two metric boxes side by side under the columns created (one showing Temperature, one showing Humidity). Using column objects directly is convenient for one-liners or if-not-inside-block usage.

The result is: a sidebar with controls, and the main page split into two columns with different content.

Using tabs and expander:

# Tabs example
tab1, tab2 = st.tabs(["Summary View", "Detailed View"])
with tab1:
st.write("This is the summary tab content.")
st.bar_chart(np.random.randn(50, 3))
with tab2:
st.write("Detailed analysis goes here (in tab 2).")
st.dataframe(df.describe())

# Expander example with st.expander("See explanation"):
st.write("""
The chart above shows random data for demonstration.
The detailed view tab contains statistical descriptions of a DataFrame.
""")

Here:

  • st.tabs(["Summary View", "Detailed View"]) creates two tabs and returns tab objects. Content inside the with tab1: block will render only when the "Summary View" tab is active, and similarly for tab2. We placed a bar chart in tab1 (for example, a summary chart) and a data summary in tab2. This allows us to organize content into tabs so the interface isn’t cluttered – the user can toggle between them.

  • After that, we create an expander. st.expander("See explanation") returns a context manager. We write a multiline string inside it. This text will be hidden under a collapsible section labeled "See explanation". The user can click the right-arrow icon to expand and read the explanation, and collapse it again. It’s useful for adding lengthy documentation or notes without overwhelming the main interface.

Performance and use notes: Layout elements themselves don’t add overhead – they are purely structural. Use them to make your app more user-friendly. For example, heavy widgets (like multiselect with lots of options) can go in the sidebar to separate controls from output. Tabs are great to compartmentalize different analysis sections or data views without spawning multiple pages. Note that all tabs share the same script execution – it’s not like separate pages, so you typically compute everything regardless of which tab is shown (though you can conditionally skip some heavy stuff based on selection).

One caveat: if you have expensive computations or large data to show, putting them in separate tabs does not automatically defer their execution until clicked. All code runs top to bottom, so both tab contents are processed on each run. If you want to run code only when a certain tab is active, you need to check the selected tab (Streamlit doesn’t give a direct tab index, but you can work around by using a state or the radio in sidebar controlling content). However, often it’s fine to precompute both.

Integration with theming: The st.set_page_config we mentioned can set layout="wide" which makes your app content area use the full browser width (the default is centered with some margins). Using wide is often nicer when you have multiple columns or large tables. You can also specify page_title and page_icon in set_page_config to set the browser tab title and favicon (nice polish for your app).

Common errors/pitfalls: One tricky thing can be column width adjustments. If you want one column narrower, you can provide relative weights like st.columns([2,1]) which means first column is double the width of second. If you see content misaligned vertically, remember that each column is its own container stream; adding elements to one column doesn’t affect the positioning in another. If you want things to line up, ensure you add the same number of elements (or use st.empty() as placeholders) in each column for that section. Another point: Once you create a column or tab, you can’t go back to adding stuff above or outside it without ending those blocks. The order of st.columns and st.tabs calls defines the structure. If you try to append to a container later, you have to either use the returned object or maintain references. For dynamic layout, you might use placeholder = st.empty() then later placeholder.columns(3) to insert columns at that spot.

Expander usage is straightforward, but note you can’t dynamically open/close it from code (except initial state via expanded=True parameter). It’s user-controlled. If you have multiple expanders or long sidebars, consider using st.sidebar.expander to group sidebar widgets as collapsible sections (yes, you can do with st.sidebar.expander("Section"): ...).

In summary, Streamlit’s layout features give you flexibility to design a clean app: use the sidebar for inputs, columns for parallel content, tabs for alternate views, and expanders for optional details. These tools help create an app that feels organized and is easy to navigate.

State and advanced interactivity

What it does: By default, Streamlit apps are stateless between runs – each rerun starts with a fresh slate of variables. Session State is a feature that allows you to store information across reruns (within a user’s session). This enables more advanced interactivity: for example, preserving counters, remembering if a user is logged in, carrying over data between widget interactions, etc. Additionally, Streamlit provides forms and widget callbacks for finer control of interactions. In this section, we cover managing state with st.session_state, using forms to batch input submissions, and mention caching (which is another form of state for performance).

Session state basics: st.session_state is like a dictionary that persists across script executions for each user session. You can use it to store and retrieve values. For instance, st.session_state["count"] = 0 initializes a counter in state, and it will remain available until the browser session ends or the app is re-run from scratch. Unlike normal variables, session_state variables won’t reset on every widget interaction.

Common patterns:

  • Initialize default state values at the top of your script (guarded by an if 'key' not in st.session_state).

  • Modify session state inside event handlers or conditionals (like incrementing a counter on button click).

  • Bind widgets to session state: Many widgets have a key argument. If you set key="name" for a text_input, its value is automatically synced to st.session_state["name"]. This can be convenient to access multiple widget values together or to prepopulate fields via st.session_state.

Example – counter and toggling with session state:

# Initialize state variables if "count" not in st.session_state:
st.session_state.count = 0 if "show" not in st.session_state:
st.session_state.show = True

st.button("Increment", on_click=lambda: st.session_state.update(count=st.session_state.count + 1))
st.button("Reset", on_click=lambda: st.session_state.update(count=0))
st.write("Counter value:", st.session_state.count)

# A checkbox that toggles visibility of some content
toggle = st.checkbox("Show details", value=st.session_state.show)
st.session_state.show = toggle  # update state based on widget if st.session_state.show:
st.write("Here are the details... (visible because checkbox is checked)")

Explanation:

  • We ensure count and show exist in session_state with initial values. This is done only on the first run for a session.

  • We create an Increment button and use the on_click parameter with a lambda function. This lambda updates the session_state count by incrementing it. When the button is clicked, Streamlit will run this callback before re-running the script, so by the time the script reruns, st.session_state.count has increased. We also have a Reset button that sets count back to 0.

  • We then display the current counter value from session_state. Unlike a normal variable, this persists, so each click truly increments or resets the stored count.

  • Next, we have a checkbox for "Show details". We initialize it with value=st.session_state.show so its default reflects the last state (initially True as set). When the user clicks it, on rerun toggle will be True/False accordingly. We immediately save st.session_state.show = toggle to keep the state updated. Then we use if st.session_state.show: to conditionally show some text. The effect is that the checkbox toggles the visibility of that details text, and thanks to session state, the choice persists on subsequent interactions or could be referenced elsewhere in the script.

  • We could have alternatively given the checkbox a key="show" instead of manually updating session_state – then Streamlit would sync it for us. For example: st.checkbox("Show details", key="show", value=True). This would create st.session_state.show and update it automatically. Either approach works; using key is more declarative and often simpler.

Forms for controlled input submission: Normally, any widget interaction triggers an immediate script rerun. Sometimes you want the user to fill multiple fields and submit them together (like a form). Streamlit provides st.form for this use case. Within a with st.form("formname"): block, you can add fields. They won’t update the app until the user clicks the form’s submit button.

Example – A simple login form:

with st.form("login_form"):
st.write("**Please log in:**")
username = st.text_input("Username")
password = st.text_input("Password", type="password")
submit = st.form_submit_button("Login")

if submit:
 if username == "admin" and password == "12345":
st.success("Logged in successfully!")
st.session_state.logged_in = True else:
st.error("Invalid credentials")
st.session_state.logged_in = False 

Explanation:

  • We open a form called "login_form". Inside, we have two text_inputs and a st.form_submit_button("Login"). These inputs will not cause the script to rerun when typing; only when the user clicks "Login" will the form be considered submitted.

  • The st.form_submit_button returns True if the form was submitted in the current run. We capture that in submit.

  • After the form block, we check if submit: and perform login logic. If credentials match, we show a success message and maybe set a session_state variable logged_in to True. If not, show an error and mark logged_in False.

  • The benefit: The user can type username and password without triggering the app to rerun on each keystroke, which might clear the password field or cause flicker. Only clicking the submit triggers the check.

  • Note that inside a form, you can’t use st.button (only form_submit_button is allowed) and you can’t call st.write after the submit button in the form context (because it won’t show until form is submitted). We did st.write before the input fields just for text prompt, which is fine. Also, widgets inside form can have keys or not as normal, but their values are only available after submit is pressed.

Callbacks: We already used on_click in the button example. Many widgets have callback parameters like on_change, on_click, along with an args or kwargs to pass to the callback. For example, a slider can do something whenever it’s changed. This is more advanced and often not needed – it’s useful when you want side-effects without waiting for full script rerun or to trigger other computations. But one should use them sparingly to keep logic clear. In most cases, checking widget values after rerun is sufficient.

Caching (for completeness in advanced interactivity): While caching is more of an optimization than interactivity, it ties into how you manage expensive operations when state changes. Streamlit has decorators @st.cache_data (for functions returning data) and @st.cache_resource (for long-lived resources like models or DB connections). These store function outputs keyed by inputs, so if the same call is made again (with same inputs and environment), it returns instantly from cache instead of recomputing. For example, you might cache a function that loads data from disk or an API so it doesn’t re-run on every widget tweak. We illustrate caching briefly:

import time

@st.cache_data def expensive_computation(x, y):
time.sleep(2)  # simulate a slow calc return x ** y

a = st.number_input("Base", value=2)
b = st.number_input("Exponent", value=10)
res = expensive_computation(a, b)
st.write(f"{a} ^ {b} = {res}")

Here, changing the inputs triggers recomputation only if (a, b) pair hasn’t been seen before. If you slide b to 11, it waits 2 seconds; slide back to 10, it returns instantly from cache. This greatly helps in interactive apps to avoid redundant work. Advanced usage: use ttl, max_entries, or clear_cache functions as needed. Also note, cached data is session-specific by default; if you want global caching across users, use @st.cache_data(show_spinner=False) plus maybe some persistence.

Common pitfalls with state: Forgetting to initialize state can cause KeyError when accessing st.session_state['foo'] that doesn’t exist. Always check or use .get(). Another pitfall is using session_state incorrectly to maintain widget values – a typical error is trying to manually set widget values via session_state in the middle of a script. Remember that at the start of a rerun, widget values come from the UI, not from your code (except initial defaults). If you programmatically update session_state for a widget’s key, it will reflect in UI only on next rerun. This is a bit advanced, but for example, to reset a text_input you could do st.session_state.name = "" in some callback and then the UI field will clear on next run because its key="name" sees a new value.

Another caveat: if using forms, widgets in the form don’t update session_state until form is submitted (or until you query them after submit). Don’t expect st.session_state.user to have the latest typed value before form submission.

Finally, be cautious with long-running callbacks (they run before script re-run but still block the main loop). If something is slow, better to let it happen during the normal run and show a spinner, etc., rather than in a callback without feedback.

By mastering session state and forms, you unlock more complex app behavior: multi-step interactions, persistent toggles, login sessions, etc. Combined with caching for performance, these make your Streamlit apps both interactive and efficient.

Advanced usage and optimization

In this section, we explore best practices to make Streamlit apps more efficient, maintainable, and robust. We’ll cover performance optimization techniques (for speed and resource usage) and general best practices for organizing code, error handling, testing, and deployment. These tips will help ensure your app runs smoothly as it grows in complexity.

Performance optimization

While Streamlit is designed for simplicity, it’s important to optimize expensive operations, especially when apps handle large data or heavy computations. Key strategies include caching results, managing computational resources, and avoiding unnecessary reruns.

  • Caching expensive computations: Use @st.cache_data to memoize functions that load data or perform heavy calculations. As introduced earlier, caching prevents recomputation when inputs haven’t changed. For example, if your app reads a 100 MB CSV or queries a database, wrap that in a cached function. This way, it runs only once per session (or per unique parameters) instead of on every widget interaction. Cached data persists in memory (by default scoped to each user’s session). You can also set ttl (time-to-live) to refresh data periodically, or max_entries to limit memory use. Remember that caching is key-sensitive – if you load a file from disk, the key might just be the file path string; if you call the same function with the same path, it will use the cached result. For truly static data, consider loading it once at top-level (outside the app function) which executes only once on server start. But for anything parameter-dependent, caching is safer. Also use @st.cache_resource for objects like ML models: e.g., loading a TensorFlow model can be cached so it doesn’t reload on each run.

  • Avoiding unneeded work on reruns: Understand which parts of your script run every time and which depend on inputs. If some code doesn’t depend on any widget, it will still run on each rerun unless cached or placed in a static context. For example, if you have a huge DataFrame and you do a complex transformation on it unconditionally, that will repeat. You might restructure code to only do heavy work inside an if that is triggered by a button or after certain inputs are set. Another technique: use session state flags to gate computations (e.g., if not st.session_state.data_loaded: load data and set flag). This way, you don’t reload data every time. Essentially, treat your Streamlit script a bit like a reactive pipeline: identify expensive steps and ensure they are executed only when necessary.

  • Managing computation and memory: If your app does iterative or real-time updates, consider using st.spinner or st.progress to give feedback instead of freezing. For instance, if you have a loop doing computations, you can update a progress bar via progress.progress(i/n). This doesn’t speed it up, but improves user experience. For parallelism: Streamlit runs each user session on a single thread (with async Tornado for I/O). If you need to do heavy multi-core computations (say run two functions in parallel), you might manually use Python’s concurrent.futures or threads inside your code. But be careful – Streamlit isn’t inherently designed for multi-threading in the script, though it can work for non-UI tasks. A simpler approach is to precompute results offline or in an initial step.

  • Memory considerations: Streamlit apps can consume memory if not careful. Large data kept in session_state will reside in RAM for the session’s life. If you cache a huge object, it stays until session ends or cache cleared, which could be an issue if many users are on. Streamlit generally handles normal usage fine, but if working with extremely large objects (like huge arrays), consider downsampling for visualization, or use generators and incremental display (though Streamlit doesn’t easily stream partial results aside from things like st.experimental_data_editor). Another trick: if you have multiple charts of essentially the same big dataset, do the heavy processing once and reuse results for each chart rather than redoing it in separate code blocks.

  • Efficient updates: Streamlit now has some support for session state callbacks and experimental single elements. For example, you can use placeholder = st.empty() to create a container, then in a loop update placeholder.table(data_chunk) repeatedly to simulate streaming data or updating without full reruns (this uses some experimental features and careful control of flow). This is advanced and rarely needed for typical apps, but can be useful for e.g. showing live data feeds or iterative algorithm progress. Always measure if such complexity is needed – often a simple approach with caching suffices.

  • Profiling and benchmarking: To identify bottlenecks, you can instrument your code. Use st.write or logging to print timestamps at various points to see what’s slow. Python’s time module or timeit can help. If a particular step is slow (say plotting a huge Altair chart), consider limiting data points. For instance, plotting 1e6 points is heavy; aggregate or sample to a few thousand for faster rendering. Also note, the browser has to render what you send – a giant HTML table will lag. So optimization isn’t just computation but also what you deliver to frontend. Paginate or summarize large outputs for performance.

Example of caching and spinner:

import time

@st.cache_data def get_simulation(n:int):
time.sleep(5)  # simulate expensive work return [x**2 for x in range(n)]

st.write("Run a heavy simulation")
N = st.slider("Size N", 1000, 5000, 1000)
if st.button("Run Simulation"):
 with st.spinner("Computing, please wait..."):
result = get_simulation(N)
st.success(f"Simulation done for N={N} (first result = {result[0]})")

In this code, clicking the button triggers a 5-second fake computation. We cache the results for each N. If you run for N=1000, wait 5s, then try N=1000 again, it’s near-instant thanks to cache. The spinner provides visual feedback during the 5-second wait. The st.success message indicates completion. Without caching, every click would incur the full wait. With caching, repeated or back-and-forth runs reuse results. This demonstrates how caching and user messaging can greatly enhance the experience in long computations.

Best practices (code organization, testing, and deployment)

Building a maintainable Streamlit app is similar to any software project – you want to keep code organized, handle errors gracefully, test critical logic, and prepare for deployment.

  • Code organization: As your app grows, avoid cramming everything into one script with sequential logic. Instead, break out reusable pieces. You can create modules for data loading, for computation, etc., and import them. Streamlit will watch these modules and auto-reload if they change, which is convenient. Use functions to encapsulate logic, especially if you’ll call them from multiple places (also functions are easier to test independently). You can even organize multi-page apps by putting pages as separate Python files in a /pages directory – Streamlit will automatically add a page navigation sidebar (this is a newer feature to support multi-page apps). This way each page’s code is in its own file. Within a file, use clear headers (st.header, etc.) to delineate sections for the user, and comments in code to denote which part does what (since Streamlit code is inherently imperative top-down, commenting helps future you or others understand the flow).

  • Error handling and user feedback: We’ve used st.error, st.warning, st.info, etc., liberally in examples. This is important – always anticipate possible failure points (file not found, wrong input type, API call fails, etc.) and inform the user. A stack trace will appear in the app by default on error, which might confuse or overwhelm end-users. Using try/except around risky operations and then showing st.error("Something went wrong: ...") is much cleaner. You can log the actual exception to console or a file if needed for debugging (Streamlit prints tracebacks to the server console if an exception isn’t caught). For user-caused issues (like invalid form input), use gentle messages (error or warning) to guide them rather than crashing. Also consider using st.stop() – this function can halt execution of the script at any point. For example, after an error message, you might call st.stop() to prevent the rest of the script (which might assume success) from running.

  • Testing: While Streamlit apps are interactive, the underlying functions can and should be tested like normal Python. You can structure computational parts as pure functions that can be unit-tested with frameworks like pytest. Streamlit itself doesn’t have a full testing framework for UI, but you can simulate interactions by calling your functions with test inputs. Another technique: factor out your Streamlit UI code from logic. For instance, have a function calculate_results(params) that you test thoroughly. Then in the app, you just do res = calculate_results(params) and display it. This separation makes both development and debugging easier. Streamlit’s team has discussed testing; there’s streamlit/testing module (experimental) and community tools that allow launching a Streamlit app in headless mode to simulate events. For most, focusing on logic testing is sufficient.

  • Documentation: Use st.help(function) in the app or refer to the official Streamlit docs for usage of components (especially new features). When writing your app, if certain controls are not self-explanatory, add st.caption or st.write text to instruct the user. For example, below a file_uploader you might put st.caption("Upload a CSV file with columns X, Y, Z"). Small hints prevent misuse. Also consider using markdown to style text – Streamlit supports emojis and icons (e.g., ✅, ❌, ℹ️) which you can include as Unicode or via Markdown shortcodes.

  • Security: If deploying publicly, be mindful of what your app can do. Streamlit by default serves on localhost; when deploying, configure it to only serve needed ports. If your app has secrets (API keys, database passwords), don’t hard-code them. Use Streamlit’s Secrets management: you can add a file .streamlit/secrets.toml and access secrets via st.secrets["key"]. Streamlit Cloud and similar platforms allow storing secrets securely. Also sanitize any user inputs if you use them in shell commands or queries to prevent injection attacks. Streamlit is not designed for authentication out-of-the-box (except in Streamlit Cloud or using third-party auth components). If auth is needed and the app is sensitive, consider running behind an authenticated proxy or implementing a simple password check (not secure for serious use, but fine for minor protection).

  • Deployment: When ready to deploy, you have options:

    • Streamlit cloud (formerly Streamlit Sharing): Very easy – just push your code to GitHub and share to Streamlit Cloud, and it will handle the environment and serve the app at a shareable URL. Free tier exists with resource limits.

    • Other PaaS: You can deploy on Heroku, AWS, Google Cloud, etc. If using Heroku or similar, you’ll typically create a requirements.txt for pip packages, maybe a Procfile with web: streamlit run app.py --server.port=$PORT --server.headless=true. On AWS EC2 or a VM, you can just run streamlit run but for production you’ll likely run it as a service (maybe via nohup or systemd) and possibly behind nginx for SSL. Streamlit is not multi-process by default (each Streamlit instance is single-process but can handle multiple users via threads). For higher scale or multi-tenancy, containerization is a good approach – build a Docker image for your app (we showed an example Dockerfile earlier). Services like AWS ECS, Google Cloud Run, or even Docker on a VM can then host it.

    • Snowflake/Streamlit integration: (Advanced) Since Snowflake acquired Streamlit, there is integration to deploy Streamlit apps inside the Snowflake data cloud. This might be enterprise-specific.

    • Local network: If you just want to share on a company network or with friends, you can run streamlit run app.py --server.address=0.0.0.0 on your machine so it’s accessible via your IP. Ensure firewall allows the port (8501 by default). This is an easy way for small-scale sharing without full deployment.

  • Monitoring and logging: On deployment, keep an eye on resource usage. Streamlit logs will show in the console – watch for any errors or unusual activity. If memory usage grows, perhaps due to large session_state usage for many users, consider periodically restarting the app or using @st.cache_data(max_entries=...) to limit cache size. There are no built-in analytics in Streamlit, but you can incorporate simple tracking (e.g., increment a counter in session_state for visits, or print logs when certain actions taken).

  • Upgrading Streamlit: The library evolves quickly (releases in 2023 added features like tabs, data_editor, etc.). Keep your Streamlit version updated to benefit from improvements (but read release notes for breaking changes). For instance, if upgrading from Streamlit 0.x to 1.x, a lot changed (like st.cache split into cache_data and cache_resource). The team maintains detailed release notes. Upgrading is usually as easy as pip install --upgrade streamlit. Newer versions often bring performance gains too.

In conclusion, treat your Streamlit app project with the same care as any software project: organize your code, handle errors, test logic, optimize performance-critical sections, and document for users. Streamlit’s simplicity makes it easy to get started, and following these best practices ensures your app remains reliable and responsive as it grows.

Real-world applications

Streamlit has been utilized in a wide array of real-world scenarios, from quick data analysis dashboards to full-blown enterprise applications. Let’s explore five case studies and examples that illustrate how the Streamlit library is applied in practice across industries and projects. These examples show the versatility of Streamlit and provide insight into metrics achieved and lessons learned.

An object detection app built with Streamlit: Users can upload an image, and the app performs real-time object detection, drawing bounding boxes around detected objects. This is an example of a computer vision tool delivered through a simple web interface.

1. Rapid NLP sentiment analyzer

Use case: A team of data scientists built a sentiment analysis tool for social media comments using Streamlit. The goal was to allow non-technical stakeholders (like marketing managers) to paste in text or upload comments and get an instant sentiment score and key phrase highlights.
Implementation: They trained an NLP model (using spaCy for key phrases and a custom classifier for sentiment). Using Streamlit, they created a text input for users to enter a comment or paragraph, and on submit, the app displays the sentiment result (positive/negative/neutral) with a color-coded highlight. Key phrases are listed and highlighted in the text. They also included an example dropdown with sample comments for quick demo. The app leverages st.cache_data to load the NLP model only once.
Metrics & Outcome: This internal tool shortened the feedback loop for content review – marketers could test copy variants and immediately see predicted audience sentiment. Over a 3-month period, the tool was used over 500 times by various teams. It helped identify negative sentiment phrases in user feedback, leading to quicker improvements in communication strategy. Performance-wise, the sentiment model inference took ~0.2s per comment, so the app felt very responsive. The ease-of-use (just a text box and results) meant no training was required for users. The project illustrates how Streamlit can turn an ML model into a useful application in hours, accelerating adoption of AI insights in business.

2. Medical image diagnostics dashboard

Use case: A healthcare startup created a Streamlit app for ultrasound image analysis. Radiologists upload an ultrasound image and the app (backed by an ML model) detects certain pathologies (e.g., tumors or lesions) and marks them on the image.
Implementation: Using Streamlit’s file uploader, users drag-and-drop an image (PNG). Upon upload, the app runs a TensorFlow model under the hood to analyze the image. Results are displayed as an overlay on the image (using st.image with drawing of bounding boxes and labels). For example, if a tumor is detected, a red box and label “Tumor (90% confidence)” is shown on the ultrasound. The app also outputs a summary table of findings (e.g., number of lesions, their approximate sizes). This project made use of custom Streamlit components for overlay (since drawing directly on images required a bit of HTML canvas work – they found a Streamlit component from community for annotations). They also utilized st.beta_set_page_config (in 2020, now just st.set_page_config) to maximize screen space for the image.
Industry Impact: This Streamlit app served as a quick validation prototype for the startup’s technology. In trials at a clinic, 5 radiologists used the app and provided feedback. They liked the immediacy of results – uploading a new image took ~3 seconds to analyze – and the clarity of the overlay. One metric: the detection model had ~85% accuracy, and the app made it simple to compare model markings with the radiologist’s own markings. The use of Streamlit saved significant development time; instead of building a web app from scratch, the team (mostly AI researchers with minimal web dev experience) got this running and deployed on a local server in days. This case shows Streamlit’s strength in rapid prototyping for high-stakes fields like healthcare, enabling subject matter experts to interact with AI results in a familiar interface (images and labels).

3. Infrastructure capacity planning tool

Use case: A large telecom company built an internal dashboard for network infrastructure planning. They have data on network usage across regions and needed to forecast when capacity upgrades are required. The Streamlit app allows interactive what-if analysis: users adjust sliders for growth rate assumptions and see projected capacity exhaustion dates and recommended upgrades.
Implementation: The app reads historical usage data (dozens of CSVs, one per region) and uses a forecasting model (prophet library) to predict future usage. Users can select a region or “All” from a dropdown. The app then displays charts: one for historical vs forecast usage, and another for capacity headroom over time. Sliders allow adjusting the annual growth percentage or adding planned new infrastructure (e.g., “add 100 GB capacity in 2024”). When sliders move, the forecast recalculates (using @st.cache_data to reuse baseline forecast and just adjust for the deltas). They also include a download button (st.download_button) so planners can download a CSV of the forecast data for offline analysis.
Metrics & Impact: Previously, analysts did this planning in spreadsheets taking hours to update; the Streamlit app reduced that to seconds. It became a one-stop tool used in quarterly planning meetings – the team projected scenarios live in the meeting by tweaking sliders, which made decision-making much faster. For instance, they identified that with a 10% higher growth than expected, three regions would exceed capacity 6 months earlier – this led to proactive investment. The app handled ~10 users simultaneously on an internal server without performance issues. This scenario highlights how Streamlit can facilitate decision support systems by combining historical data, forecasting, and interactive scenario testing in real-time.

4. COVID-19 data explorer

Use case: During the COVID-19 pandemic, many data scientists shared public dashboards. One impressive Streamlit application was a COVID-19 data explorer for Europe. It allowed users to select countries and metrics (cases, deaths, vaccination rates) and visualize trends and comparisons.
Implementation: The app fetched daily data from an open API (disease.sh or Our World in Data) – using requests inside st.cache_data so it wouldn’t overload the API or slow the app. Users could pick one or multiple countries from a multiselect. The app then showed time-series line charts of cases and deaths, with options to switch between linear and log scale. It also had a map view (using st.map or pydeck) to show latest stats by region. This app utilized tabs: e.g., one tab for “Trends” (line charts) and another for “Map”. It also made heavy use of st.plotly_chart for interactive zoomable charts.
Community Impact: This Streamlit app was shared widely; it provided policymakers and the public with an accessible way to see how different countries were faring. One could, for example, select Germany, France, Italy and quickly see if case curves were flattening or not. The log scale toggle was important to compare growth rates, and the Streamlit interface made it easy (just a checkbox or radio to switch scale). Metrics: at its peak, the app had thousands of daily viewers (hosted on Streamlit Sharing). It demonstrated Streamlit’s capability for fast deployment of informational dashboards in crises, where data clarity and timeliness were crucial. Even with high traffic, the app’s simplicity (caching data and using Streamlit Cloud’s infrastructure) kept it running smoothly. The developers noted the quick development time – what might have taken weeks to build a polished dashboard in another framework took just days with Streamlit’s high-level API.

5. Renewable energy forecasting and monitoring

Use case: A renewable energy company created RenewCast, a Streamlit app to forecast solar and wind power generation in various locations. This helped them and their clients understand expected energy output and plan grid usage.
Implementation: The app interface allowed selecting a country and energy type (solar PV or wind). It then pulled historical generation data and weather patterns. Using the sktime library (for time-series forecasting), it generated a forecast for the next 30 days of energy output. The results were shown in line charts with prediction intervals. They also had a feature to compare actual vs forecast when new data comes in – an interactive date slider let users scroll through time to see how forecasts matched reality. To manage performance, heavy computations (like training the model or updating forecasts) were done offline daily; the Streamlit app mainly loaded and displayed those precomputed forecasts (making liberal use of st.cache_data). An expander provided technical details on the forecasting algorithms for transparency.
Results: This app was used by energy analysts and even exposed to some external partners. It improved visibility into how weather translates to energy – for example, utility companies used it to anticipate lower solar output on upcoming cloudy weeks and balance with other sources. The case study showed that Streamlit can be used for scientific and engineering applications where complex models are involved, yet the end interface is straightforward. The open-source nature allowed them to integrate niche libraries (like sktime) easily. Over time, they even connected the app to live weather API updates: with a click of a “Refresh forecast” button, the app would fetch the latest weather data, rerun the model (this took ~15 seconds, during which a spinner showed), and update the charts. This dynamic capability — running an ML model on demand in an app — underscores Streamlit’s power for interactive ML.

Each of these examples – from NLP sentiment tools to infrastructure dashboards – demonstrates how the Streamlit library enables rapid development and deployment of useful applications. The common theme is that domain experts (whether in ML, healthcare, energy, or public policy) were able to create and share interactive tools without needing a full software engineering team or complex web development. Streamlit’s simplicity, paired with Python’s rich ecosystem, accelerates the journey from data or model to decision-making. These real-world applications also show that Streamlit apps can scale from personal projects to widely used dashboards, as long as best practices (caching, efficient code, etc.) are followed. The success of these cases has further driven Streamlit’s adoption as a go-to solution for turning scripts into shareable, impactful apps.

Alternatives and comparisons

Streamlit is one of several frameworks available for building data apps and dashboards in Python. Depending on the project requirements, an alternative might be more suitable. Here we compare Streamlit with a few popular alternatives – Plotly Dash, and Gradio across key factors. We’ll also discuss when to use which, and provide guidance on migrating existing apps from one framework to another.

Comparison Table of Streamlit vs Alternatives:

AspectStreamlit (st)Plotly DashGradio
Ease of use / learning curveVery easy – simple API, pure Python script. Little boilerplate (just st. calls). Great for beginners.Moderate – requires understanding layout (HTML) and callbacks. More setup code (app layout, callbacks for interactivity). Steeper for beginners.Very easy – designed for ML demos, has high-level blocks (Interface). Minimal code to get a UI for models.
Features & ComponentsRich set of basic widgets (input, file, etc.) and easy integration with data plots (matplotlib, Altair, etc.). Lacks built-in complex components, but community components exist. Emphasizes rapid dev over fine control.Very comprehensive – supports complex, customizable layouts, multi-page apps. Extensive support for Plotly graphs, D3 via Dash components. More low-level control (CSS styling) possible. Has tables, upload, etc., plus enterprise addons.Focused feature set – primarily for model I/O (text, image, audio inputs; outputs for those). Provides prebuilt interfaces for vision, audio, etc. Less general-purpose UI components than Streamlit/Dash.
Performance & scalabilityGood for small to medium apps. Single-threaded per session but can handle multiple users (each session a thread). Heavy compute should be cached or offloaded. Not designed for massive concurrent use, but fine for dozens of users or internal tools.Good for larger apps – built on Flask, can be deployed on multi-worker servers for scale. More suited to enterprise scaling (especially with Dash Enterprise). Tends to handle callbacks efficiently but requires careful design to avoid bottlenecks. Overhead of network roundtrips for each interaction (which is fine for most cases). Dash can outperform Streamlit in scenario with many simultaneous users due to its stateless callback model.Good for its niche – typically single-user ML demo scenarios or small scale. Not optimized for many concurrent users. Each Gradio interface runs as a single process by default (though can be scaled with Kubernetes for example). For demo and prototyping, performance is usually sufficient (inference time dominates).
Community & supportRapidly growing community. Excellent official docs and many examples. Streamlit is open-source, and after acquisition by Snowflake it has strong backing and frequent releases. Community forum is active (topics on everything from deployment to hacks).Very large community (Dash has been around since 2017). Plotly provides extensive documentation and examples for Dash. There’s an active forum for Dash as well. Enterprise customers get official support. Many third-party Dash components available (e.g., dash_bootstrap_components for styling).Growing in ML community due to ease for model demos (especially since Hugging Face adopted it for model sharing). Documentation is straightforward, and it’s open source. Not as large a general-purpose community as Streamlit or Dash, but strong niche usage for AI demo apps.
Licensing & costOpen-source (Apache 2.0). Completely free to use. Streamlit Cloud has free tier and paid plans for more resources, but the library itself has no cost.Open-source core (MIT license). Dash can be used freely. Plotly offers Dash Enterprise (commercial) with additional features (auth, deployment ease, etc.) – that costs money, but optional.Open-source (Apache 2.0). Free. Gradio also offers a hosted option via HuggingFace Spaces (free for public demos, or private Spaces with subscription) – but the library is free to run anywhere.
When to use (Strengths)Use Streamlit for rapid prototyping, data exploration, and internal apps where speed of development is paramount. Great for ML demos, simple dashboards, forms, and when you want to use pure Python without worrying about frontend code. Strengths: very low barrier to entry, fast iteration, beautiful default styling. Ideal for sharing analyses with non-technical stakeholders quickly.Use Dash for complex, polished dashboards especially if you need intricate layouts, custom callbacks between components, or to embed in an existing web app. Dash shines when you need fine control over appearance and behavior – you can dive into HTML/CSS/JS if needed. It’s suitable for production apps that may require authentication, deployment scaling, etc. (especially with Dash Enterprise). Choose Dash if your app demands custom UI components or if you love Plotly graphs – integration is seamless.Use Gradio for machine learning model demos and UI. It’s specifically geared to taking a Python function (model inference) and wrapping a web UI around it with minimal effort. Ideal for showing off a new model to colleagues or on a blog – e.g., a face recognition demo where you upload an image and get prediction. Strengths: simplicity for ML I/O, quick integration with HuggingFace for sharing. Not meant for multi-page or generalized dashboards – it’s more for interactive widgets hooked to functions.

Table: A high-level comparison between Streamlit library and alternative Python dashboarding frameworks.

Migration guide

If you have an existing app in another framework and want to migrate to Streamlit (or vice versa), here are some tips:

  • From Dash to Streamlit: Identify the core layout and callbacks in Dash. In Streamlit, you will replace the explicit layout definition with top-down code. For example, a Dash dropdown + graph callback becomes simply option = st.selectbox(...) followed by st.plotly_chart(fig_for_option). Instead of Dash’s @app.callback structure, you rely on Streamlit’s automatic rerun. One pitfall: variables don’t persist as in Dash, so use st.session_state if needed. Overall, migrating simple apps is straightforward – you’ll often see a reduction in lines of code by ~30-50%. For complex multi-component apps, consider breaking into multiple Streamlit pages or tabs for clarity. Also, note that some Dash components (like Dash DataTable) don’t have 1-1 equivalents in Streamlit yet, though Streamlit’s st.data_editor (introduced in 2022) covers many table editing use cases. Migrating those might require using a Streamlit community component or simplifying the functionality. Example code translation – a basic Dash app:

    # Dash example (excerpt)
    app.layout = html.Div([
    dcc.Dropdown(id='city', options=[...], value='NYC'),
    dcc.Graph(id='weather-chart')
    ])
    @app.callback(Output('weather-chart', 'figure'), [Input('city', 'value')])
    def update_chart(city):
    fig = create_weather_fig(city)
     return fig

    Equivalent in Streamlit:

    city = st.selectbox("City", ["NYC", ...])
    fig = create_weather_fig(city)
    st.plotly_chart(fig)

    As shown, the logic is much simpler. The Dash app might refresh only the graph when city changes, whereas Streamlit refreshes the whole script – but since this script is short and create_weather_fig can be cached if heavy, the difference is negligible.

  • From Streamlit to dash/panel: If you find you need more customization (or you need to integrate into a larger web context), migrating from Streamlit to Dash or Panel is a bit more involved. You’ll have to define an explicit layout (Dash HTML or Panel objects) and callbacks. Essentially, invert the control flow: instead of sequential script, you declare layout components and their interdependencies. One tip: use the stateful logic you had in Streamlit (session_state usage) as a guide for what needs to persist or be passed between callbacks. Migrating to Panel, if coming from Streamlit, you might structure your code into classes with Param (Panel’s parametric objects) to hold state and define reactive methods. This can be a paradigm shift, so weigh if migration is necessary or if Streamlit can still meet your needs via components or upcoming features.

  • Using both (hybrid approach): In some cases, you might embed Streamlit apps within other contexts or call out to dash apps, but generally mixing frameworks in one page is not feasible. It’s usually better to stick to one framework per app to avoid complexity.

  • Component libraries: All frameworks allow custom components. Streamlit has the components API where you can write a bit of frontend code if truly needed. Before migrating just to get a certain widget, check if a Streamlit component exists – e.g., if you needed an editable ag-grid table from Dash, there is a Streamlit AgGrid component available. The community ecosystem may fill your gap without full migration.

Pitfalls during migration: One must re-think session handling – e.g., Dash is stateless between callbacks (except what you store in dcc.Store or in the layout), whereas Streamlit session_state is stateful. If you migrate to Dash, you might need to explicitly maintain state (e.g., using hidden divs or dcc.Store for multi-step forms). Another pitfall is URL routing – Dash and Panel allow multi-page via routes, whereas Streamlit now has a simpler pages directory mechanism. If migrating a multi-page Streamlit app to Dash, you’d implement it with Dash’s multi-page support (since Dash 2.0+, they have a pages plugin) or use dcc.Location and callbacks to switch views.

In summary, each framework has its niche:

  • Streamlit library Python – best for quick development, data apps, ML demos, and internal tools where simplicity and dev speed trump pixel-perfect design.

  • Dash – best for more complex, possibly client-facing dashboards where you need control and are willing to write more code (and possibly use enterprise features).

  • Gradio – best for machine learning demo apps, especially for sharing models on Hugging Face or similar.

  • Panel – best for power users in scientific Python who want integrate with Jupyter or need the flexibility to use different plotting backends and have granular control.

By understanding these differences, you can choose the right tool for the job or migrate your project at the appropriate time. Remember, the ultimate guide to Streamlit library emphasizes that for the majority of simple to moderately complex apps, Streamlit offers a hard-to-beat combination of ease and capability – which is why it has gained such popularity in 2024–2025.

Resources and further reading

For those looking to deepen their Streamlit knowledge or seek help, here are some valuable resources:

  • Official Streamlit documentation – The official docs cover all commands, tutorials, and an API reference. Start here for fundamentals and advanced features.

  • Streamlit cheat sheet – A handy reference of common commands and usage examples, available on the docs site (often updated with new features).

  • Streamlit community forum – An active forum where you can ask questions, find answers to common problems, and see showcase apps. The Streamlit team and community members are very responsive.

  • Streamlit blog – Official blog with announcements, case studies, and tips. Great for staying updated on new releases or learning how others use Streamlit.

  • Streamlit on YouTube – The Streamlit team has a YouTube channel with tutorial videos and recorded live streams. For example, the "Streamlit Crash Course" video is a quick way to get started.

  • Awesome Streamlit GitHub repository – A curated list of Streamlit apps, components, and resources by the community (Marc Skov Madsen). Browsing this can inspire and provide snippets for your own app.

  • Streamlit components gallery – Directory of community-developed components (from 3D visualization to advanced widgets). Found on the Streamlit website.

  • Book – Data Science Applications with Streamlit by Marc-Andre Dufour (2022) – A comprehensive guide and use-cases in book format, covering various apps and best practices.

  • Gradio vs Streamlit Article – Comparison article for those specifically interested in ML demos (search for "Streamlit vs Gradio for ML apps").

  • Plotly Dash vs Streamlit – Kanaries blog – An in-depth article comparing Dash and Streamlit (useful if you are deciding between them).

  • HoloViz discourse for panel – If you are exploring Panel, the HoloViz forum and docs are the go-to.

  • Deployment guidesStreamlit Deployment Guide (official docs) and community tutorials for deploying on various platforms (Heroku, AWS, Docker).

  • Streamlit for Teams/Snowflake – Snowflake’s resources on integrating Streamlit with Snowflake data warehouse, if you use that ecosystem.

Lastly, consider joining the community on social media:

  • The Streamlit tag on Stack Overflow for development Q&A.

  • Follow the official Twitter/X account @streamlit for updates and tips (many examples and new app shoutouts are posted there).

  • The Streamlit subreddit (r/Streamlit) occasionally has discussions and showcases.

By leveraging these resources, you can continue improving your Streamlit skills, troubleshoot issues, and stay at the forefront of new features and best practices. Happy app building!

FAQs

Finally, here is an extensive list of Frequently asked squestions (with concise answers) covering installation, basic usage, features, troubleshooting, optimization, integration, best practices, and comparisons. This comprehensive FAQ should address most questions new users have about the Streamlit library:

Installation and setup

  1. Q: How do I install the Streamlit library via pip?
    A: Run pip install streamlit in your terminal. This will download Streamlit and its dependencies from PyPI. Make sure you have Python 3.8+ installed. After installation, you can verify by running streamlit --version.

  2. Q: How to install Streamlit in Anaconda/conda?
    A: You can install via conda-forge channel. Activate your conda env and run: conda install -c conda-forge streamlit. This ensures you get the latest version and compatible dependencies.

  3. Q: Do I need Node.js or other front-end tools to use Streamlit?
    A: No, you don’t. Streamlit handles front-end (it comes with a React-based UI). You just write Python – no need for Node, npm, or writing HTML/JS unless you are making advanced custom components.

  4. Q: What Python versions are supported by Streamlit library?
    A: Streamlit supports Python 3.7 through 3.12 (as of 2025). It is recommended to use Python 3.8+ for best compatibility. Python 2 is not supported.

  5. Q: How do I run a Streamlit app after installation?
    A: Use the Streamlit CLI. For example, if your script is app.py, run streamlit run app.py in your terminal. This will start a local web server and open the app in your browser.

  6. Q: Streamlit command not found – how to fix?
    A: This usually means the installation’s bin folder isn’t in your PATH. If using pip, ensure you’re running in the environment where Streamlit is installed. On Windows, you might need to close/reopen terminal. Alternatively, run with Python: python -m streamlit run app.py to bypass PATH issues.

  7. Q: Can I install Streamlit in a virtual environment?
    A: Yes, and it’s recommended. Create a venv or use conda env, then pip install streamlit inside it. Streamlit will then be isolated to that environment along with any other packages you install.

  8. Q: How to install Streamlit in Jupyter or Colab?
    A: You can !pip install streamlit in a notebook, but Streamlit is designed to run as a separate web app, not inside the notebook UI. In Colab, you can install but you won’t easily see the interface inline (there are hacks, but generally you run Streamlit separately). For local Jupyter, consider using the jupyter-streamlit extension or just run the script normally.

  9. Q: How do I update/upgrade the Streamlit library to the latest version?
    A: Use pip: pip install --upgrade streamlit. Or conda: conda update streamlit -c conda-forge. Always good to read release notes after upgrading since new features or deprecations may occur.

  10. Q: Is Streamlit available for installation via apt/homebrew?
    A: Not directly via apt or brew. It’s easiest to use pip/conda. However, you can use Homebrew to install Python itself, then pip for Streamlit. There is no native apt-get for streamlit since it’s a Python package, not a system package.

  11. Q: Do I need a GPU or special hardware for Streamlit?
    A: No specific hardware is required for Streamlit itself. It runs on CPU. If your app uses GPU libraries (like TensorFlow), you’ll need compatible hardware, but Streamlit library doesn’t have GPU acceleration or requirements.

  12. Q: Can I install Streamlit on Raspberry Pi or ARM devices?
    A: Yes, as long as Python 3 is available. Many have successfully run Streamlit on Raspberry Pi (install via pip). Performance is lower due to Pi’s limited resources, but for simple apps it works.

  13. Q: How to set up Streamlit in VS Code?
    A: Use VS Code’s terminal to install streamlit in your environment. Then you can create a debugging configuration: set it to run streamlit run ${file} for the current script. Alternatively, run from terminal. VS Code doesn’t have built-in Streamlit integration, but you can use the browser for viewing. Ensure VS Code is using the correct Python interpreter where Streamlit is installed.

  14. Q: How to configure PyCharm to run Streamlit apps?
    A: In PyCharm, you can add a Run Configuration. Choose to run streamlit as the script (give full path to streamlit if needed, or just streamlit if it’s in PATH), and add parameters: run app.py. Set working directory appropriately. Then running this config will launch the app. Alternatively, just use PyCharm’s terminal.

  15. Q: What’s the easiest way to share a Streamlit app with someone else?
    A: If it’s a small-scale share, you can run streamlit run on your machine and tell them to connect to your IP (might require firewall adjustments). For broader sharing, deploy to Streamlit Community Cloud (just push to GitHub and create a Streamlit Cloud app – free for public apps). Or deploy to Heroku, etc. For quick internal sharing, hosting on an internal server or VM with streamlit running is straightforward.

  16. Q: Do I need to open a specific port for Streamlit?
    A: By default Streamlit uses port 8501. If running on a server, ensure port 8501 is accessible (or specify another port via --server.port). When deploying on platforms like Heroku, you’ll use whatever port they assign via $PORT env.

  17. Q: How to run Streamlit on a different port?
    A: Use the --server.port flag, e.g. streamlit run app.py --server.port=8502. Or set in config. This is useful if 8501 is occupied or you want multiple apps at once.

  18. Q: Where are Streamlit’s configuration files and what can I configure?
    A: Streamlit has a global config file (typically ~/.streamlit/config.toml). You can configure things like the theme, browser behavior, server options, etc. For example, to enable wide mode by default: add layout="wide" under [theme] or use st.set_page_config. To prevent browser from opening on run: server.headless = true. Config can also be set via environment variables or CLI flags.

  19. Q: How do I set a custom page title and favicon for my app?
    A: Use st.set_page_config(page_title="My App", page_icon="🔥") at the top of your script. This sets the browser tab title and favicon (you can use an emoji or path to an icon image).

  20. Q: Is it possible to run multiple Streamlit apps on one server?
    A: Yes, you can either run them on different ports (start multiple processes) or use Streamlit’s built-in multi-page feature (since v1.10) to have one app with multiple pages in a sidebar. If you need them separate, just run each with a different port or host behind a reverse proxy that routes to each app’s port.

  21. Q: Streamlit installation is stuck at “Collecting pytz / tornado / etc.” – what do I do?
    A: Sometimes pip might hang on dependency resolution. Ensure you have the latest pip (pip install --upgrade pip). If using conda, environment solve can hang – adding -c conda-forge helps. If internet issues, check connectivity. Most often upgrading pip or specifying pip install streamlit==X.Y to avoid dependency solver issues can help.

  22. Q: I installed Streamlit but import streamlit fails in Python – why?
    A: This usually means the package isn’t installed in the Python environment you’re using. Double-check you installed in the right interpreter. In a notebook, you might need to restart the kernel after pip installing. If using PyCharm/VS Code, make sure the interpreter matches where you did pip install.

  23. Q: Can I install and run Streamlit on Windows Subsystem for Linux (WSL)?
    A: Yes, you can pip install streamlit in WSL. Running streamlit run in WSL will by default open a browser in Windows (through a special localhost link). This generally works well – many develop in WSL and view in Windows browser.

  24. Q: Does Streamlit library work offline / without internet?
    A: Absolutely. Once installed, you do not need internet to run a Streamlit app on localhost. All UI assets are served from the Streamlit package. (Internet would only be required if your app’s code fetches online data or for some components that rely on CDN – but by default Streamlit assets are bundled, so offline is fine.)

  25. Q: How to install a specific older version of Streamlit?
    A: Use pip with the version: pip install streamlit==1.15.2 (for example). This will install that exact version. You might need this for compatibility if something changed in latest version that you aren’t ready to adapt to.

  26. Q: Is it possible to have multiple versions of Streamlit installed side by side?
    A: Only via separate environments. E.g., create venv for v1.20 and another for v1.10. You can’t have two versions in one environment at same time. Use virtualenv or conda env to manage this.

  27. Q: I get an error “Cannot open browser” when running on a remote server. How to handle this?
    A: Streamlit tries to open a browser by default. On a headless server with no browser, you’ll see a warning. It will still serve the app, you just have to open it manually via URL. To suppress the attempt, run in headless mode: streamlit run app.py --server.headless true or set server.headless=true in config.

  28. Q: Can I run Streamlit library within a Docker container?
    A: Yes. Use a base image (like python:3.10-slim), install streamlit, copy your app code. Expose port 8501. Use CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]. Many examples of Streamlit Dockerfiles exist (including official docs). It’s a common deployment method.

  29. Q: After installing, why does streamlit hello run fine but my script doesn’t open a new browser tab?
    A: If you run streamlit run app.py and nothing opens, possibly Streamlit couldn’t detect your browser or you used --server.headless. Check the terminal output – it should print “You can now view your Streamlit app at http://localhost:8501”. Copy that URL to your browser manually. Also ensure you don’t have browser.serverAddress or other config mis-set (defaults usually work).

  30. Q: Do I need to install any libraries for specific features (like plotly or altair)?
    A: If you plan to use a specific plotting library with Streamlit, you need to install that library. Streamlit itself only includes basic matplotlib. For Altair charts, install altair; for Plotly, pip install plotly. Streamlit will detect and work with them if available. Similarly for other integrations (e.g., pandas should be installed to use DataFrame, etc., but that’s usually already present in data environments).

Basic usage and syntax

  1. Q: How do I create text output in a Streamlit app?
    A: Use functions like st.write(), st.text(), st.markdown(), st.title(), st.header(), etc. For example, st.write("Hello world") will display text. st.markdown("**bold text**") allows Markdown formatting. There are also st.latex() for math and st.code() for code blocks.

  2. Q: What is the difference between st.write and st.text?
    A: st.write is very versatile – it can auto-render different types (text, plots, DataFrames, etc.) and it interprets Markdown if you pass a string with formatting. st.text simply outputs raw text exactly as given (monospace font). For normal usage, st.write is convenient; use st.text if you want to show text without any formatting interpretation (e.g., to display code or HTML as plain text).

  3. Q: How can I display a pandas DataFrame or numpy array?
    A: Simply use st.write(df) to display a DataFrame as an interactive table (with sorting, scrolling). Or use st.dataframe(df) for more options (like highlighting or width control). st.table(df) will show a static table (good for small tables). Numpy arrays can be converted to DataFrame or you can just st.write(array) (it will show as list).

  4. Q: How do I create a button and know when it’s clicked?
    A: Use clicked = st.button("Do something"). This renders a button. It returns True on the immediate rerun after click, otherwise False. So you typically do:

    if st.button("Greet"):
    st.write("Hello!")

    On click, that block executes. Note the script runs top-to-bottom each time, so the if ensures code inside runs only on that particular run.

  5. Q: How do I get user input, like a text input or slider?
    A: Use widgets like st.text_input("Label"), st.number_input("Label"), st.slider("Label", min, max, default), etc. These functions return the value. For instance:

    name = st.text_input("Your name:")
    st.write("Hello", name)

    This will show a text box. As user types, name updates after each key press (causing rerun), and the greeting updates.

  6. Q: Can I hide or show elements dynamically (e.g., when a checkbox is checked)?
    A: Yes. Just use conditional logic in your script. For example:

    show_details = st.checkbox("Show details")
    if show_details:
    st.write("Detailed info: ...")

    Since the script reruns on checking/unchecking, the if controls whether that content appears. This is a common pattern for toggling sections.

  7. Q: What’s the proper way to do a multi-step form (like asking several inputs then submit)?
    A: You have two approaches: use st.form to batch input and submit at once (so changes don’t apply until user hits submit), or manage it with session state flags. E.g., first show step 1 inputs, on submit set a flag to show step 2 inputs. The easier is with st.form("myform"): – put fields and a st.form_submit_button("Submit"). After the form, check if submitted and proceed.

  8. Q: How does Streamlit’s “rerun on each interaction” work exactly?
    A: Whenever a widget state changes (like user input), Streamlit triggers a rerun of the entire script from top. But it preserves widget states (so you don’t lose what was typed/selected). This means your code should be written idempotently – each run should produce the same interface given the same states. Embrace this model; it’s like a reactive notebook. If you need to avoid heavy recompute, use caching or put things under conditions.

  9. Q: How can I display a Matplotlib or Plotly chart?
    A: For Matplotlib:

    import matplotlib.pyplot as plt
    fig, ax = plt.subplots()
    ax.plot([...])
    st.pyplot(fig)

    For Plotly:

    import plotly.express as px
    fig = px.line(data_frame, x=..., y=...)
    st.plotly_chart(fig)

    Streamlit will embed the interactive Plotly chart. Altair charts can be shown with st.altair_chart(chart). In general, Streamlit has specialized functions for popular chart libs.

  10. Q: How to display images or videos in Streamlit?
    A: Use st.image("path_or_image_object") for images. It accepts PIL images, numpy arrays, or URL/paths. You can specify caption and use_column_width=True to scale. For videos, use st.video("video.mp4") or a YouTube link. For audio, st.audio("file.mp3"). These will render HTML5 video/audio players in the app.

  11. Q: Can I customize the style or theme of my Streamlit app?
    A: Streamlit has theming support. By default it has a theme following system preferences. You can set st.set_page_config(theme="dark") or configure in ~/.streamlit/config.toml (like picking primary colors, etc.). For heavy customization, currently you can inject CSS via st.markdown("<style>...</style>", unsafe_allow_html=True) but that’s not officially endorsed. They have gradually expanded theming options (like setting font, background, etc., in config). But it’s not as flexible as using your own HTML/CSS, since Streamlit’s layout is somewhat fixed.

  12. Q: How do I organize my code? Can I use functions and multiple files?
    A: Absolutely use functions to break logic. Streamlit will happily call your functions. Just avoid relying on global variables for app state (use session_state instead). You can also import other Python files: e.g., from utils import load_data and use load_data() in your app. If those files change, Streamlit auto-reloads by default (except if you disable that). For very large apps, consider using the multi-page feature (put other scripts in a pages/ folder with a title prefix). Each page is basically its own Streamlit script.

  13. Q: What is st.session_state and when do I need it?
    A: st.session_state is a dictionary-like object to store variables that persist across reruns in a user session. You need it when you want to preserve information – e.g., a counter, or inputs that shouldn’t reset. Also, widget values with key can be accessed via session_state. Use it for multi-step flows or to store computed results so they don’t recompute each run. It’s essentially how you manage state, given Streamlit’s rerun model.

  14. Q: How do I initialize or reset session state values?
    A: You can check if a key exists and set it:

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

    This ensures one-time init. To reset, you can simply assign a new value (or st.session_state.clear() to wipe all, though use carefully). Also, some widgets (like a button) have an argument on_click where you can provide a callback to update state, which is a neat way to do reset on a button press.

  15. Q: How do I display an error or warning message to the user?
    A: Use st.error("message") for an error (red box with a cross icon), st.warning("message") for a warning (yellow box), or st.info("message") for neutral info (blue box). For success messages (green check), use st.success("message"). These help communicate results or issues gracefully.

  16. Q: My app logic requires waiting for multiple inputs before doing something – how can I avoid partial runs?
    A: Either use st.form to collect inputs then submit (preferred), or you can wrap the logic in a condition that only runs when all required inputs are provided. E.g.,

    age = st.number_input("Age")
    color = st.selectbox("Color", options)
    if age and color:
    ...do stuff...

    This way until both are selected, the main action doesn’t execute. But the form approach is cleaner for multi-field submission.

  17. Q: How can I make certain parts of the app update without refreshing everything?
    A: By design, Streamlit refreshes the whole script. But you can manage partial updates using placeholders. For example:

    placeholder = st.empty()
    placeholder.line_chart(data1)
    # ... later ...
    placeholder.line_chart(data2)

    This will replace the chart in place. Similarly, st.experimental_rerun() can be used to restart script early if needed. However, if you find yourself wanting fine-grained updates, remember that Streamlit’s simplicity is in full reruns; optimizing via caching or selective logic is usually enough.

  18. Q: What is the best way to do navigation or multi-page apps?
    A: The easiest is using the built-in Multi-Page support. Just create a pages directory, put files like 1_Introduction.py, 2_Analysis.py. Streamlit will show a sidebar with those page names (numbers ensure order). Alternatively, you could simulate navigation with a selectbox:

    page = st.sidebar.selectbox("Page", ["Intro", "Analysis"])
    if page == "Intro": ...
    else: ...

    But that can get messy in one file. Multi-page (introduced in Streamlit 1.10) is straightforward and recommended for larger apps.

  19. Q: How can I pass data between pages in multi-page app?
    A: You might put common data in st.session_state so all pages can access it (session_state is shared across pages of one session). Or cache data-loading functions so both pages use the same cached result. There’s currently no official URL parameter passing (though you can use st.experimental_set_query_params as a hack). The simplest: load or compute data in each page (with caching to avoid duplication) or set state on one page and read it on another.

  20. Q: What are some must-know Streamlit commands for beginners?
    A: Key ones:

    • st.write() (general display),

    • st.text_input, st.number_input, st.slider, st.selectbox, st.checkbox, st.radio (common inputs),

    • st.button and st.form/st.form_submit_button,

    • st.dataframe for data, st.chart functions for quick plots (line_chart, bar_chart, map),

    • st.image, st.audio, st.video for media,

    • st.spinner for loading indications,

    • st.session_state for persistence,

    • st.set_page_config for initial page setup,

    • and layout functions like st.columns, st.expander, st.tabs, st.sidebar. Mastering these will allow you to create most app structures.

  21. Q: How do I add a download link or button for users to get data (e.g., CSV)?
    A: Use st.download_button(label, data, file_name). For example:

    csv = df.to_csv().encode('utf-8')
    st.download_button("Download data as CSV", csv, "data.csv", "text/csv")

    This will offer a download of the given bytes with the specified filename and MIME. If you prefer, an anchor tag hack via markdown can also be used, but st.download_button is the straightforward way.

Features and functionality

  1. Q: What types of charts can Streamlit create out-of-the-box?
    A: Streamlit has st.line_chart, st.area_chart, and st.bar_chart for quick plots from data. These are high-level and use Vega-Lite under the hood. For more complex charts, use libraries: Matplotlib/Seaborn (with st.pyplot), Plotly (st.plotly_chart), Altair (st.altair_chart), Bokeh (st.bokeh_chart), or others. Streamlit supports many chart types indirectly through these libs. There’s also st.map for a quick scatter map of latitude/longitude points (renders a Mapbox map).

  2. Q: Can I make plots interactive (zoom, hover, etc.) in Streamlit?
    A: Yes. If you use Plotly, Bokeh, or Altair, those charts are interactive (zoom, pan, tooltips). st.plotly_chart(fig) by default allows interaction (you can set use_container_width=True to fit better). Matplotlib charts displayed via st.pyplot are static images (no interactivity). So choose an interactive plotting library for interactive charts. Also, some community components add interactivity (e.g., Deck.GL maps, or Folium maps via components).

  3. Q: How do I create an interactive data table (sortable, filterable) in Streamlit?
    A: st.dataframe(df) gives an interactive table: columns can be sorted by clicking headers, it’s scrollable and can be subset by dragging (not full filtering though). If you need more, Streamlit introduced st.data_editor (formerly experimental, now in stable after 1.18) – it allows editing table cells and filtering. For very advanced tables (like those with formulas or extremely large data), a specialized component like AgGrid might be used via st_aggrid component.

  4. Q: Can users upload files through the app? What types are supported?
    A: Yes, using st.file_uploader. You can specify allowed types (e.g., type=["csv","png"]). It supports any file basically, delivering it as a file-like object or bytes. Commonly used for CSVs, images, PDFs, etc. For images, you might directly do Image.open(uploaded_file). Upload limit by default is 200MB (can be configured). Uploaded files are kept in memory (not written to disk unless you do so).

  5. Q: Is it possible to have two columns of widgets/text side by side?
    A: Yes, use st.columns. E.g., col1, col2 = st.columns(2) then col1.button("Yes"), col2.button("No"). Anything done with colX. is placed in that column. Columns can also be used to layout charts and text side-by-side. If you need a 3 or 4-column layout, just extend the list in st.columns(3) etc. You can adjust width ratios by using st.columns([2,1]) as weights.

  6. Q: How do I add an expanding/collapsing section (for optional details)?
    A: Use st.expander("Section Name") as a context manager:

    with st.expander("More info"):
    st.write("This is hidden by default, visible when expanded.")

    This appears as a collapsed section that user can expand to reveal content.

  7. Q: Can I display colored or styled text (e.g., red warning text or bold)?
    A: Yes, via Markdown or HTML. E.g., st.markdown("<span style='color:red'>Important</span>", unsafe_allow_html=True). Or simply st.error("Important") which already styles it with red background. For bold, use **bold** in markdown. Streamlit’s built-in methods cover common styling (error/warning/info/success messages, headers, etc.) so you rarely need raw HTML.

  8. Q: What is the purpose of st.form and how is it different from grouping inputs normally?
    A: st.form allows you to create a form that does not auto-submit on every widget change. Normally, each widget triggers a rerun when interacted with. Inside a form, widget interactions don’t trigger reruns until the user clicks the submit button. This is useful for entering multiple fields (like login example). So use forms to batch input and only process when ready.

  9. Q: How can I execute code on button click without re-running all my code above the button?
    A: Short answer: you cannot prevent the rerun – that’s how Streamlit works. But you can structure your code so that heavy parts are cached or skip if not needed. For instance, place expensive setup above the button under if 'setup_done' not in st.session_state: so it doesn’t repeat, or simply rely on caching. When a button is clicked, everything runs again – accept that and code accordingly (use if statements to gate what runs). Streamlit ensures the end effect is like the button triggered only the code under it (provided you structure checks using its return value). st.button returning True means “this run is triggered by click”, so you can differentiate logic.

  10. Q: How to stop or prevent Streamlit from running code further (like a break)?
    A: Use st.stop(). This will halt execution immediately. E.g.,

    if not valid_input:
    st.error("Invalid input.")
    st.stop()

    Then the rest of script won’t execute in that run. Another related function is st.experimental_rerun() which stops and restarts the script from top (useful if you manually update session_state and want to immediately refresh UI).

  11. Q: Can I use Streamlit to make a real-time dashboard that auto-refreshes (without user input)?
    A: You can simulate this by adding something like:

    import time
    while True:
    update_data_and_charts()
    time.sleep(5)
    st.experimental_rerun()

    However, this will tie up server thread. Alternatively, use streamlit-autorefresh component or instruct users to set the refresh interval via config. Streamlit is not push-based – it’s pull. Usually, you rely on user input to refresh. But for real-time, a hacky solution is possible (though not the most efficient). If you have streaming data and want to constantly update a chart, one technique is to use st.empty() and update inside a loop (as long as session is open), but you need to break the loop if user disconnects. This is advanced. For moderate “auto-refresh every minute”, the simplest is st.experimental_rerun() in a loop or using meta-refresh via HTML (embedding a <meta http-equiv="refresh" content="60"> using unsafe_allow_html to refresh page).

  12. Q: How do I integrate user authentication (login) in Streamlit?
    A: Streamlit doesn’t have built-in auth. You have to implement it manually (for example, a simple username/password form and then restrict content if not logged in – storing logged_in flag in session_state). For enterprise, one might run Streamlit behind an authenticated proxy (like OAuth2 proxy or SSO via network). Some community solutions exist (packages like streamlit-authenticator). On Streamlit Cloud, you can restrict viewers by email domain if on certain plans. But by default, handle it in-app: e.g.,

    if 'logged_in' not in st.session_state:
     # show login form else:
     # show app content 

    Once correct credentials entered, set st.session_state.logged_in = True.

  13. Q: Can I use multiple Streamlit widgets in one line (like label, input on same line)?
    A: Yes, use st.columns. For example:

    col1, col2 = st.columns([1,2])
    col1.write("Name:")
    col2.text_input("Enter name", key="name")

    This places the label in col1 and input in col2, appearing side by side. There’s no direct parameter in text_input to put label on left (it’s always above by default), so columns is the way.

  14. Q: Is it possible to hide the hamburger menu or the “Made with Streamlit” footer?
    A: Yes, you can use config options. In .streamlit/config.toml:

    [theme] base="light"
    ...
    [browser] gatherUsageStats = false [ui] hideMenu = true hideToolbar = true 

    Setting ui.hideMenu removes the hamburger, ui.hideToolbar removes the top-right “Run” icon and such. The “Made with Streamlit” footer for apps on Streamlit Cloud free tier is not removable except by upgrading plan (for self-hosted there is no such footer). Alternatively, one could hide via CSS injection:

    st.markdown("<style>#MainMenu {visibility: hidden;} footer {visibility: hidden;}</style>", unsafe_allow_html=True)

    But official way is config.

  15. Q: What are Streamlit components and how can I use them?
    A: Components are a way to extend Streamlit with custom frontend elements. Many components (made by community) can be installed via pip and used like import streamlit_component_xyz as xyz; xyz.some_function(). For example, streamlit-aggrid provides an interactive grid, streamlit-webrtc for webcam, etc. Using them is typically as easy as calling their function and treating output like any widget value. If you want to create your own, you would use React or another JS to build a component and integrate (this is advanced and you’d follow Streamlit’s component template docs). But you likely don’t need to build one – check the component marketplace first.

  16. Q: How to display progress of a loop in the app (progress bar)?
    A: Use st.progress. E.g.,

    progress = st.progress(0)
    for i in range(100):
    do_step(i)
    progress.progress(i+1)
    st.write("Done!")

    This will show a progress bar filling from 0 to 100%. You could also use with st.spinner("Doing work..."): to show a message with a spinning icon during a block of code. Combine both for full effect if needed.

  17. Q: Can Streamlit do plotting in real-time (e.g., updating a chart within a loop)?
    A: Yes, via placeholders. For example:

    import numpy as np
    placeholder = st.empty()
    chart = st.line_chart(np.random.randn(10,2))
    for _ in range(10):
    time.sleep(1)
    new_data = np.random.randn(1,2)
    chart.add_rows(new_data)

    This uses chart.add_rows which is a special method for live-updating a chart’s data. This only works for st.line_chart, st.area_chart, st.bar_chart objects (and using add_rows on them). For more custom charts, you would likely refresh by re-drawing in a loop with a placeholder (less efficient). The above code appends random data points every second, creating a dynamic chart.

  18. Q: How to handle input validation in Streamlit?
    A: You handle it in Python after inputs. For instance,

    age = st.number_input("Age:", min_value=0, max_value=120)
    if st.button("Submit"):
     if age < 18:
    st.error("Must be at least 18.")
     else:
    st.success("Age accepted.")

    For text, you might check pattern or length and display error. Because of rerun, ideally do validation on submit rather than on every keystroke (to avoid error flicker). But you could do inline validation by an if not valid: st.warning("format wrong") that would appear as they type.

  19. Q: How to make parts of text dynamic (e.g., value that changes)?
    A: You can use f-strings or string formatting inside st.write/markdown. E.g.:

    score = 42
    st.markdown(f"Your score is **{score}** out of 50.")

    When score changes (and script reruns), the text updates. There’s also st.metric(label, value, delta) which is specifically for showing a changing numeric value with an arrow indicating delta (commonly used for KPIs).

  20. Q: Does Streamlit support charts with multiple subplots or complex layouts?
    A: Not directly with a single command, but you can create them via Matplotlib or Plotly and just display. For example, a Matplotlib figure with multiple subplots can be shown via st.pyplot(fig). Plotly can also create subplots (using make_subplots in plotly) and st.plotly_chart will render it. So yes, but you do it by those libraries’ capabilities.

  21. Q: Can I embed one Streamlit app inside another or embed external web content?
    A: You can embed external content via st.components.v1.iframe or st.markdown with an <iframe> tag (with unsafe_allow_html). For example, showing a YouTube video via st.video(youtube_link) actually uses an iframe. For arbitrary embed (like embedding a Google map or another site), you could do
    python st.components.v1.iframe("https://example.com", width=700, height=500)  

    Embedding one Streamlit app inside another isn’t typical. Better to merge into pages or use hyperlinks to switch apps. Technically you could iframe a Streamlit app if it’s hosted somewhere and allow embedding, but there might be header constraints (Streamlit sets X-Frame-Options to sameorigin by default, so might need config to allow iframing from other origin).

Resources

Katerina Hynkova

Blog

Illustrative image for blog post

Ultimate guide to uv library in Python

By Katerina Hynkova

Updated on August 22, 2025

That’s it, time to try Deepnote

Get started – it’s free
Book a demo

Footer

Solutions

  • Notebook
  • Data apps
  • Machine learning
  • Data teams

Product

Company

Comparisons

Resources

Footer

  • Privacy
  • Terms

© 2025 Deepnote. All rights reserved.