Enterprise DNA

Omni by Enterprise DNA

Enterprise DNA Resources

Step-by-step how-tos. Practical AI operating-system thinking for owners, operators, and teams doing real work.

220k+

Data professionals

Omni

AI agents and apps

Audit

Map the manual work

Guide Intermediate General

Streamlit AI App Tutorial for Beginners

Build your first AI-powered Streamlit app with real working code, auth setup, and deployment patterns that actually work.

Sam McKay |
Streamlit AI App Tutorial for Beginners

What Streamlit AI actually is

Streamlit is an open-source Python framework that turns Python scripts into interactive web applications. When people say “Streamlit AI,” they usually mean one of two things: using Streamlit to build the front-end for an AI application, or Streamlit’s own AI-assisted features for app development.

The framework itself is pure Python. You write a script, run it with streamlit run, and a browser tab opens with your app. Every time a user interacts with a widget, Streamlit reruns your script top to bottom. This model is unusual if you come from React or Flask, but it makes data and ML workflows feel like writing a notebook rather than building a web app.

For AI apps specifically, Streamlit ships native chat elements: st.chat_message for rendering messages, st.chat_input for the prompt box, and st.write_stream for streaming tokens. These were added in the 1.24 release and they removed most of the friction that used to exist when building chat interfaces in Streamlit. Before that, you had to fake a chat UI with text areas and markdown.

Under the hood, Streamlit handles the WebSocket connection, the rerun cycle, and the rendering. You bring your own model. That model might be OpenAI’s API, a local Ollama instance, a Hugging Face endpoint, or anything else that returns text. Streamlit does not bundle an LLM. It is not a hosted AI service. It is a Python framework that happens to be very good at displaying AI outputs.

Setup and authentication

You need Python 3.9 or newer and pip. The install is one command.

pip install streamlit

Verify the install with streamlit hello. This opens a demo gallery in your browser and confirms the framework is wired up correctly. If the browser does not open automatically, copy the URL from the terminal output, typically http://localhost:8501.

For an AI app you also need an LLM client. The most common setup is OpenAI’s Python SDK:

pip install openai

API keys go in a file called .streamlit/secrets.toml in your project root. Streamlit reads this file at runtime and exposes values through st.secrets. The structure looks like this:

OPENAI_API_KEY = "sk-..."

In your code, access it with st.secrets["OPENAI_API_KEY"]. Never hardcode keys in your script. Streamlit also reads environment variables, so for deployment platforms like Streamlit Community Cloud you can use either approach.

For local development, create a .gitignore entry for .streamlit/secrets.toml on day one. The framework will warn you if secrets are exposed, but the warning is easy to miss in a busy terminal. Treat any committed key as compromised and rotate it immediately.

First working example

Here is a minimal chat app that talks to an OpenAI model. Save it as app.py in a project folder that contains a .streamlit/secrets.toml file with your API key.

import streamlit as st
from openai import OpenAI

client = OpenAI(api_key=st.secrets["OPENAI_API_KEY"])

st.set_page_config(page_title="Chat", layout="centered")
st.title("Simple Chat")

if "messages" not in st.session_state:
    st.session_state.messages = []

for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

if prompt := st.chat_input("Say something"):
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.markdown(prompt)

    with st.chat_message("assistant"):
        stream = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[{"role": m["role"], "content": m["content"]} for m in st.session_state.messages],
            stream=True,
        )
        response = st.write_stream(stream)
    st.session_state.messages.append({"role": "assistant", "content": response})

Run it with streamlit run app.py. The first load takes a few seconds while Streamlit starts the server. Subsequent interactions are fast because the script only reruns the parts that depend on changed widgets.

The key building blocks are:

  • st.set_page_config for the browser tab title and layout
  • st.title for the header
  • st.chat_message with a role of “user” or “assistant” to render each turn
  • st.chat_input to capture the next prompt, using the walrus operator for inline assignment
  • st.session_state to keep the message list across reruns
  • client.chat.completions.create with stream=True to get tokens back one at a time
  • st.write_stream to render those tokens as they arrive

If you see the chat input but no response, check the terminal where you launched the app. Streamlit prints exceptions there, not in the browser. This is the most common gotcha for first-time users.

Key settings that matter

Most beginners ignore the configuration knobs that separate a toy app from a usable one. Here are the ones that matter.

Caching with @st.cache_data and @st.cache_resource. When you call an LLM API, you do not want to re-authenticate or reload system prompts on every rerun. @st.cache_resource is designed for clients like the OpenAI SDK that should be created once and reused. @st.cache_data is for deterministic function results you want to memoize. Use them aggressively. A chat app that reinitializes its client on every keystroke will feel slow and may hit rate limits.

Session state for conversation history. Streamlit reruns your script on every interaction. Without st.session_state, your message list disappears every time the user clicks anything. Initialize the history once with a default of an empty list, then append to it inside the chat input handler.

Secrets management. Beyond the local secrets.toml file, Streamlit Community Cloud lets you set secrets through the dashboard. For self-hosted deployments, mount secrets as environment variables and read them with os.environ. The framework treats both paths the same way through st.secrets.

Page config and theme. st.set_page_config accepts a theme parameter for primary color and background. This is the difference between an app that looks like a 2010 dashboard and one that looks intentional. Set it once at the top of the script.

Layout options. st.sidebar is useful for model selection, temperature sliders, and system prompt editing. Putting controls in the sidebar keeps the main chat area clean and gives you a place to add features later without redesigning the layout.

Wide mode. st.set_page_config(layout="wide") gives you more horizontal space for tables, charts, and side-by-side comparisons. Default layout is centered and narrow, which suits chat apps but cramps anything data-heavy.

Where it shines

Streamlit is genuinely the fastest way to turn a Python script into something a non-developer can use. The specific scenarios where it works well:

Internal tools for data teams. If your team lives in pandas and SQL, Streamlit lets you ship a tool in an afternoon that replaces a spreadsheet workflow. The user gets a URL, the URL opens an app, the app calls your existing code.

ML model demos. Showing a stakeholder what a model does is the original Streamlit use case. Drop a trained model into a script, add a few input widgets, and you have a demo that runs locally or on a free hosted tier.

AI chat interfaces. The native chat elements make this the lowest-friction way to prototype a chatbot. You can swap model providers, test prompt variations, and add retrieval-augmented generation without leaving Python.

Quick prototypes for user testing. When you need to know whether an idea works before investing in a real frontend, Streamlit gets you to a testable artifact in hours. The rerun model is honest about what your code does, which makes iteration fast.

Embedding in Snowflake or Databricks. Both platforms have first-party Streamlit support. If your data already lives there, you can build an app that queries it directly without moving data around.

Where it fails

The rerun model is also Streamlit’s biggest weakness. Every interaction re-executes your script from the top, which creates real problems in certain situations.

Complex multi-step workflows. If your app needs to track state across many steps, manage long-lived connections, or coordinate between multiple users, the rerun model becomes a fight. You will end up building workarounds with session state that feel like fighting the framework.

Production-grade public apps. Streamlit Community Cloud is fine for demos and internal tools. For a customer-facing product with thousands of users, you need a real backend, real auth, and real infrastructure. Streamlit can be part of that stack, but it should not be the whole thing.

Mobile-first design. Streamlit apps are not responsive in the way modern web apps are. The layout system works on a laptop and breaks on a phone. If mobile is a primary surface, build a proper frontend.

Heavy compute on the main thread. If your AI call takes 30 seconds, the user sees a frozen page. Streamlit does have async