Skip to content

1. Streamlit basics

Beginner · 8 min read

1.1 Your first app

Save as app.py:

app.py
import streamlit as st

st.title("Prompt playground")
st.write("Type a prompt and see how long it is.")

prompt = st.text_area("Prompt", "Explain RAG in one sentence.")
st.write(f"**{len(prompt.split())} words**, about {len(prompt) // 4} tokens")

Run it from a terminal:

streamlit run app.py         # opens http://localhost:8501

Edit the file and save — the browser shows Rerun (or turn on Always rerun in the ☰ menu).

1.2 The rerun model — the one idea to understand

Every time the user touches a widget, Streamlit runs your whole script again, top to bottom. Each widget call returns its current value.

flowchart LR
    U[User types / clicks] --> R[Script reruns from line 1]
    R --> W[Widgets return current values]
    W --> D[Page redrawn]
    D --> U

That makes code simple (no callbacks, no event handlers) but has two consequences:

  1. Normal variables reset on every rerun. To remember something (chat history, a counter), use st.session_state — see Layout, state & caching.
  2. Slow work reruns too. Loading a model or embedding a PDF on every click is slow and costly — cache it.
rerun_demo.py
import streamlit as st

count = 0                                   # resets to 0 on every rerun!
if st.button("Click me"):
    count += 1
st.write("Plain variable:", count)          # never goes above 1

if "clicks" not in st.session_state:        # survives reruns
    st.session_state.clicks = 0
if st.button("Click me too"):
    st.session_state.clicks += 1
st.write("session_state:", st.session_state.clicks)

1.3 Showing text

text.py
import streamlit as st

st.title("Title")
st.header("Header")
st.subheader("Subheader")
st.markdown("**Markdown** with `code`, [links](https://streamlit.io) and lists:\n- one\n- two")
st.caption("Small grey text — model name, latency, cost")
st.code("from openai import OpenAI\nclient = OpenAI()", language="python")
st.latex(r"\text{cosine}(a, b) = \frac{a \cdot b}{\|a\|\|b\|}")
st.divider()
st.write("`st.write` accepts almost anything:", {"model": "gpt-4o-mini"}, [1, 2, 3])

st.write is the Swiss-army knife: strings become Markdown, dicts become JSON viewers, DataFrames become tables.

1.4 Showing data

data.py
import pandas as pd
import streamlit as st

runs = pd.DataFrame({
    "model": ["gpt-4o-mini", "claude-haiku", "llama-3-8b"],
    "accuracy": [0.86, 0.84, 0.71],
    "latency_s": [1.2, 0.9, 0.6],
    "cost_per_1k": [0.15, 0.25, 0.0],
})

c1, c2, c3 = st.columns(3)
c1.metric("Best accuracy", "86%", "+2% vs last week")
c2.metric("Avg latency", "0.9 s", "-0.3 s", delta_color="inverse")
c3.metric("Eval questions", 250)

st.dataframe(runs, hide_index=True)            # sortable, scrollable table
st.bar_chart(runs, x="model", y="accuracy")
st.json({"model": "gpt-4o-mini", "usage": {"prompt_tokens": 812, "completion_tokens": 96}})

st.dataframe is interactive; st.table is static; st.data_editor lets the user edit the table — handy for labelling eval data.

1.5 Widgets — getting input

Every widget returns its value directly:

widgets.py
import streamlit as st

model = st.selectbox("Model", ["gpt-4o-mini", "claude-sonnet-5-5", "llama-3-70b"])
temperature = st.slider("Temperature", 0.0, 2.0, 0.2, step=0.1)
max_tokens = st.number_input("Max tokens", min_value=16, max_value=4096, value=512, step=16)
stream = st.toggle("Stream tokens", value=True)
tools = st.multiselect("Tools the agent may use", ["web_search", "calculator", "sql"], default=["calculator"])
system = st.text_area("System prompt", "You are a concise assistant.", height=100)
question = st.text_input("Question", placeholder="Ask something…")

if st.button("Run", type="primary", disabled=not question):
    st.write({"model": model, "temperature": temperature, "max_tokens": max_tokens,
              "stream": stream, "tools": tools, "question": question})
Widget Returns
st.text_input, st.text_area str
st.number_input, st.slider int / float (or a tuple for a range slider)
st.selectbox, st.radio the chosen option
st.multiselect, st.pills list of chosen options
st.checkbox, st.toggle bool
st.button True only on the rerun right after the click
st.file_uploader an uploaded file object or None

Buttons don't stay pressed

st.button is True for one rerun only. Anything you show inside if st.button(...) disappears on the next interaction. Save results in st.session_state if they must stay on screen.

1.6 Status and feedback

status.py
import time
import streamlit as st

with st.spinner("Calling the LLM…"):
    time.sleep(1)                       # stand-in for the API call
st.success("Answer ready")
st.info("Using the cached index")
st.warning("Context is 90% full — older messages will be dropped")
st.error("Rate limited by the provider. Try again in 20 s.")

with st.status("Agent working…", expanded=True) as status:
    st.write("🔍 Searching the knowledge base")
    time.sleep(0.5)
    st.write("🧮 Calling the calculator tool")
    time.sleep(0.5)
    status.update(label="Agent finished", state="complete")

st.toast("Saved to history")

st.status is perfect for showing an agent's steps while it works.

1.7 Stopping early

guard.py
import streamlit as st

api_key = st.text_input("OpenAI API key", type="password")
if not api_key:
    st.info("Enter your API key to continue.")
    st.stop()                           # nothing below runs until there is a key
st.write("Key received — ready to chat.")

Practice

  • Build a "token counter" app: a text area plus metrics for characters, words and estimated tokens.
  • Add a selectbox of models and show the estimated cost using a price-per-1K-tokens dict.

Next: Layout, state & caching — sidebar settings, memory between reruns, and fast apps.