Backend / Web frameworks / Streamlit / 01_basics_and_widgets.md

Streamlit Basics and Widgets - Interview Questions

Updated 3 interview angles 4 min read source
On this page11
  1. 1. How do you install and run a Streamlit app?
  2. 2. What are the main widget types?
  3. 3. How does st.button differ from other widgets?
  4. 4. How do you lay out content?
  5. 5. How do you display data?
  6. 6. What’s the difference between st.write and st.markdown / st.text?
  7. 7. How do you upload and process a file?
  8. 8. How do you show progress for a long operation?
  9. 9. How do you handle forms (batch widget input)?
  10. 10. Cross-links
  11. Interview angle

Streamlit Basics and Widgets - Interview Questions

1. How do you install and run a Streamlit app?

bash
pip install streamlit
streamlit run app.py

That starts a local server (default http://localhost:8501) with a WebSocket connection back to the script. Editing app.py triggers a “Source file changed — Rerun?” prompt; auto-rerun is in Settings.

To run on a different port or bind address:

bash
streamlit run app.py --server.port 8080 --server.address 0.0.0.0

2. What are the main widget types?

Widget Returns Use for
st.button("X") bool (True only on the rerun after click) One-shot actions
st.text_input("X") str Single-line text
st.text_area("X") str Multi-line text
st.number_input("X") int/float Numbers with step/min/max
st.slider("X", 0, 100) int/float/tuple Range selection
st.selectbox("X", opts) option Single-choice dropdown
st.multiselect("X", opts) list Multi-choice
st.radio("X", opts) option Visible single-choice
st.checkbox("X") bool Toggle
st.date_input / st.time_input date / time Date/time pickers
st.file_uploader("X") UploadedFile or None File upload
st.color_picker("X") hex str Color
st.toggle("X") bool Like checkbox, switch UI

Every widget call returns its current value. The script reruns when the value changes.

3. How does st.button differ from other widgets?

st.button returns True only on the single rerun immediately after the click, then snaps back to False. Other widgets hold their value across reruns until the user changes them.

python
if st.button("Save"):
    save_to_db()           # runs once, on the click rerun
    st.success("Saved!")   # gone on the next rerun

# Anti-pattern: this won't work the way you expect
clicked = st.button("Click")
# typing here reruns the script
st.text_input("Name")
                           # → clicked is False again, "Click" forgotten

To remember a click, write to st.session_state:

python
if st.button("Start"):
    st.session_state["started"] = True

if st.session_state.get("started"):
    show_workflow()

4. How do you lay out content?

python
import streamlit as st

# Sidebar
with st.sidebar:
    page = st.radio("Section", ["A", "B"])

# Columns
col1, col2, col3 = st.columns([2, 1, 1])  # ratio
with col1:
    st.metric("Revenue", "$1.2M")
with col2:
    st.metric("Users", "12,300")
with col3:
    st.metric("Latency", "230ms")

# Tabs
tab1, tab2 = st.tabs(["Overview", "Details"])
with tab1:
    st.write("Summary view")
with tab2:
    st.dataframe(df)

# Expander
with st.expander("Show raw data"):
    st.json(payload)

# Container — group elements, return a handle you can write into later
placeholder = st.container()
placeholder.write("This appears here, even if added later.")

st.empty() reserves a slot you can overwrite — useful for replacing a “Loading…” placeholder with the final result.

5. How do you display data?

python
st.write(anything)               # smart dispatcher: dict→json, df→table, str→markdown
st.dataframe(df)                 # interactive table, sortable, scrollable
st.table(df)                     # static, simpler, no virtualization
st.metric("Revenue", "$1M", "+5%")  # KPI card with delta
st.json(my_dict)
st.code("print('hi')", language="python")
# default is safe; html disabled
st.markdown("**bold**", unsafe_allow_html=False)
st.image("logo.png", width=200)
st.video(url_or_bytes)
st.audio(bytes)

For charts:

python
st.line_chart(df)            # built-in, fast, limited
st.bar_chart(df)
st.area_chart(df)
st.altair_chart(chart, use_container_width=True)
st.plotly_chart(fig, use_container_width=True)
st.pyplot(fig)               # matplotlib

Plotly is the default for “real” charts because it ships interactivity (hover, zoom) without extra work.

6. What’s the difference between st.write and st.markdown / st.text?

  • st.write(x) — smart. Dispatches based on type: dict→json, pd.DataFrame→table, str→markdown, plotly figure→chart, etc. Good for “just show me this.”
  • st.markdown(s) — explicit markdown. Use when you know it’s text and want full control over formatting.
  • st.text(s) — fixed-width plain text, no markdown rendering. Use for log lines or anything where you don’t want *foo* to become italic.

In production code, prefer the explicit form (markdown/dataframe/plotly_chart) — st.write is great for prototyping but ambiguous at review time.

7. How do you upload and process a file?

python
uploaded = st.file_uploader("Upload CSV", type=["csv"])
if uploaded is not None:
    # uploaded behaves like a file object
    df = pd.read_csv(uploaded)
    st.dataframe(df)

    # For binary files / re-reads:
    raw = uploaded.read()                 # bytes
    uploaded.seek(0)                      # reset pointer if you read it again

Multiple files:

python
files = st.file_uploader("Upload", accept_multiple_files=True)
for f in files:
    process(f)

Defaults: 200 MB per file. Override via .streamlit/config.toml[server] maxUploadSize = 1024 (in MB).

8. How do you show progress for a long operation?

python
with st.spinner("Loading..."):
    result = slow_function()

# or a numeric progress bar
bar = st.progress(0, text="Processing...")
for i, item in enumerate(items):
    process(item)
    bar.progress((i + 1) / len(items), text=f"{i+1}/{len(items)}")
bar.empty()

# Status block (1.31+) — collapsible, shows steps
with st.status("Running pipeline...", expanded=True) as status:
    st.write("Downloading data...")
    download()
    st.write("Training model...")
    train()
    status.update(label="Done", state="complete", expanded=False)

# Toast — small ephemeral notification (3s)
st.toast("Saved!", icon="")

For long jobs, also consider running them outside the Streamlit script (Celery / a queue) and polling status — Streamlit ties up a WebSocket per running script.

9. How do you handle forms (batch widget input)?

By default, every widget change reruns the script. For a form (multiple inputs that should only submit together), use st.form:

python
with st.form("user_form"):
    name = st.text_input("Name")
    age = st.number_input("Age", 0, 120)
    submitted = st.form_submit_button("Save")

if submitted:
    save_user(name, age)

Inside st.form, widget changes don’t trigger reruns — only the submit button does. This is the right tool for any “fill out fields, then submit” interaction.

Interview angle 3

  • “How does a widget’s value persist across reruns?” - by its key. Streamlit identifies widgets by position and parameters unless you pass an explicit key, which is why conditionally rendered widgets can lose or swap state.
  • “Why does my widget reset unexpectedly?” - its identity changed. Adding a widget above it, or changing its label, changes the auto-generated key. Explicit keys fix it.
  • “How do you avoid work on every keystroke?” - st.form batches inputs so the script only re-runs on submit, and caching handles the expensive parts elsewhere.