Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions skills/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Streamlit Agent Skills

This directory contains agent skills for building Streamlit applications. These skills are designed to help AI coding assistants provide better guidance when developers are building Streamlit apps.

## Available Skills

| Skill | Description | Lines |
|-------|-------------|-------|
| [streamlit-best-practices](./streamlit-best-practices/) | Opinionated coding conventions and anti-patterns | 252 |
| [building-streamlit-apps](./building-streamlit-apps/) | Core execution model, session state, widget patterns, caching basics | 189 |
| [optimizing-streamlit-performance](./optimizing-streamlit-performance/) | Caching strategies, fragments, forms, large data handling | 202 |
| [building-streamlit-chat-apps](./building-streamlit-chat-apps/) | Chat interfaces, LLM integration, streaming responses | 217 |
| [displaying-streamlit-data](./displaying-streamlit-data/) | DataFrames, charts, metrics, column configuration | 228 |
| [designing-streamlit-layouts](./designing-streamlit-layouts/) | Columns, sidebar, tabs, dialogs, status elements | 243 |
| [building-streamlit-multipage-apps](./building-streamlit-multipage-apps/) | Navigation, cross-page state, URL parameters | 181 |
| [connecting-streamlit-data](./connecting-streamlit-data/) | Database connections, st.connection, secrets | 152 |
| [securing-streamlit-apps](./securing-streamlit-apps/) | Secrets management, OIDC authentication, security | 220 |
| [testing-streamlit-apps](./testing-streamlit-apps/) | AppTest framework, pytest integration | 253 |

## Skill Format

Each skill follows the [Agent Skills specification](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview):

- **YAML frontmatter** with `name` and `description`
- **Concise instructions** (all files under 500 lines)
- **Code examples** showing good vs bad patterns
- **Reference links** to official documentation

## Usage

These skills can be used with:
- [Claude Code](https://code.claude.com/)
- [Claude Agent SDK](https://docs.anthropic.com/en/agent-sdk/)
- [Anthropic API](https://docs.anthropic.com/)
- [Cursor](https://cursor.com/)
- Other AI coding assistants that support agent skills

## Skill Triggering

Skills are triggered based on their descriptions. Key trigger phrases for each:

- **streamlit-best-practices**: "best practices", "conventions", "code quality", "anti-patterns"
- **building-streamlit-apps**: "Streamlit app", "session state", "widget", "rerun"
- **optimizing-streamlit-performance**: "slow", "performance", "cache", "large data"
- **building-streamlit-chat-apps**: "chat", "chatbot", "LLM", "OpenAI", "streaming"
- **displaying-streamlit-data**: "dataframe", "chart", "metric", "visualization"
- **designing-streamlit-layouts**: "layout", "columns", "sidebar", "tabs", "dialog"
- **building-streamlit-multipage-apps**: "multipage", "navigation", "pages"
- **connecting-streamlit-data**: "database", "connection", "SQL", "API"
- **securing-streamlit-apps**: "secrets", "authentication", "security", "login"
- **testing-streamlit-apps**: "test", "pytest", "AppTest", "CI/CD"

## Contributing

When updating skills:
1. Keep files under 500 lines
2. Use concise language (Claude already knows basics)
3. Provide good/bad code examples
4. Include links to official docs
5. Test with multiple Claude models (Haiku, Sonnet, Opus)
162 changes: 162 additions & 0 deletions skills/building-streamlit-apps/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
---
name: building-streamlit-apps
description: Builds Streamlit web applications with proper state management, caching, and widget patterns. Use when creating Streamlit apps, handling session state, managing widget behavior, or encountering unexpected reruns or state issues.
---

# Building Streamlit Apps

Streamlit reruns the entire script top-to-bottom on every user interaction. This execution model requires specific patterns for state management and widget behavior.

## Session State

Variables reset on every rerun unless stored in `st.session_state`.

```python
# BAD: Lost on rerun
count = 0
count += 1

# GOOD: Persists across reruns
if "count" not in st.session_state:
st.session_state.count = 0
st.session_state.count += 1
```

**Key rules:**
- Always check existence before accessing: `if "key" not in st.session_state`
- Use `st.session_state.get("key", default)` for safe access with defaults
- Session state is per-user, per-tab, and temporary (lost on tab close)

## Widget Keys and State

Widgets can store values in session state via the `key` parameter.

```python
# Widget value accessible via session state
name = st.text_input("Name", key="user_name")
# st.session_state.user_name contains the same value
```

Use the widget's `key` in callbacks, not the return variable:

```python
# BAD: Updates every second click
def increment():
slide_val += 1 # Wrong - using variable

# GOOD: Use the key
def increment():
st.session_state.slider += 1 # Correct - using key

st.button("Add", on_click=increment)
slide_val = st.slider("Value", 0, 10, key="slider")
```

## Button Behavior

Buttons return `True` only during the rerun triggered by the click. They do NOT retain state.

```python
# BAD: Content disappears after any other interaction
if st.button("Load data"):
data = load_data() # Lost on next rerun!
st.dataframe(data)

# GOOD: Store result in session state
if st.button("Load data"):
st.session_state.data = load_data()

if "data" in st.session_state:
st.dataframe(st.session_state.data)
```

**Anti-patterns to avoid:**
- Nested buttons never work (outer button is `False` when inner clicked)
- Widgets inside `if st.button()` blocks disappear after any interaction
- Don't set `st.session_state.my_button` - button state is not settable

**Toggle pattern:**

```python
if "show_details" not in st.session_state:
st.session_state.show_details = False

def toggle():
st.session_state.show_details = not st.session_state.show_details

st.button("Toggle details", on_click=toggle)

if st.session_state.show_details:
st.write("Details here...")
```

## Caching Basics

Cache expensive operations to avoid recomputation on every rerun.

```python
@st.cache_data # For data: DataFrames, API responses, computations
def load_data():
return pd.read_csv("large_file.csv")

@st.cache_resource # For resources: DB connections, ML models
def get_model():
return load_ml_model()
```

**When to use which:**
- `@st.cache_data`: Returns a copy each call. Safe for data that might be modified.
- `@st.cache_resource`: Returns the same object. Use for connections/models.

**Important:** Set `ttl` or `max_entries` for data that changes to prevent unbounded memory growth.

```python
@st.cache_data(ttl=3600) # Refresh after 1 hour
def get_live_data():
return fetch_from_api()
```

## Callbacks

Use callbacks for immediate state changes before the rerun:

```python
def on_change():
st.session_state.processed = process(st.session_state.input_text)

st.text_input("Enter text", key="input_text", on_change=on_change)

# processed value available immediately in this rerun
if "processed" in st.session_state:
st.write(st.session_state.processed)
```

## Custom Classes

Classes defined in the main script are redefined on each rerun, breaking identity checks.

```python
# BAD: Class redefined each rerun
class User:
def __init__(self, name):
self.name = name

# isinstance() and == checks may fail unexpectedly
```

**Solutions:**
1. Define classes in separate modules (imported modules aren't redefined)
2. Implement custom `__eq__` and `__hash__` methods
3. Use dataclasses with `frozen=True`
4. Store serializable data (dicts) instead of class instances

## Common Gotchas

1. **Duplicate widget keys**: Each widget needs a unique key if used multiple times
2. **Modifying cached data**: Don't mutate `@st.cache_resource` returns (affects all users)

## Reference

- [Session State docs](https://docs.streamlit.io/develop/concepts/architecture/session-state)
- [Caching docs](https://docs.streamlit.io/develop/concepts/architecture/caching)
- [Widget behavior](https://docs.streamlit.io/develop/concepts/architecture/widget-behavior)
Loading