A newer version of the Gradio SDK is available:
6.1.0
P3 Bug: Progress Bar Positioning/Overlap in ChatInterface
Severity: P3 (Low - UX polish) Status: Open Discovered: 2025-12-01 Reporter: Internal QA
Symptom
The gr.Progress() bar renders in a strange position when used inside ChatInterface:
- Progress bar appears to "float" in the middle of the chat output
- Text overlaps with progress bar elements
- Bar appears static/stuck at certain percentages
- Visual artifact:
Round 2/5 (~2m 15s remaining) - 48.0%renders both above and inside bar
Screenshot Evidence
Progress bar appears inline between chat messages rather than in a fixed position (top/bottom of component).
Root Cause Analysis
The Conflict
We're mixing two different progress mechanisms:
gr.Progress()- Gradio's general-purpose progress bar API- Designed for
gr.Interfaceandgr.Blocksfunctions - Renders as an overlay on the output component
- Designed for
ChatInterface.show_progress- Built-in chat progress- Options:
"full","minimal","hidden" "full"= spinner + runtime display"minimal"= runtime display only (default)
- Options:
When both are used together in ChatInterface, the gr.Progress() bar fights for position with the chat's streaming output, causing visual glitches.
Current Implementation (src/app.py)
async def research_agent(
message: str,
history: list[dict[str, Any]],
...
progress: gr.Progress = gr.Progress(), # <- ISSUE: Manual progress bar
) -> AsyncGenerator[str, None]:
...
if event.type == "started":
progress(0, desc="Starting research...") # <- Updates overlay
elif event.type == "progress":
progress(p, desc=event.message) # <- Conflicts with chat streaming
Gradio Documentation
From Gradio ChatInterface Docs:
show_progress: how to show the progress animation while event is running: 'full' shows a spinner which covers the output component area as well as a runtime display in the upper right corner, 'minimal' only shows the runtime display, 'hidden' shows no progress animation at all
From GitHub Issue #5967:
gr.Progressis "not integrated with ChatInterface or Chatbots" - known limitation.
Impact
| Aspect | Impact |
|---|---|
| Functionality | None - app works correctly |
| UX | Visual confusion, looks unprofessional |
| Accessibility | May cause screen reader confusion |
Proposed Solutions
Option 1: Remove gr.Progress() - Use Chat Text Only (RECOMMENDED)
Remove the gr.Progress() parameter entirely and rely on our existing emoji status messages:
async def research_agent(
message: str,
history: list[dict[str, Any]],
domain: str = "sexual_health",
api_key: str = "",
api_key_state: str = "",
# REMOVED: progress: gr.Progress = gr.Progress(),
) -> AsyncGenerator[str, None]:
...
# Keep emoji status updates in chat output
# ⏱️ **PROGRESS**: Round 1/5 (~3m 0s remaining)
# These are already being yielded to the chat
Pros:
- Simplest solution
- No CSS hacks
- Status is visible in chat history
- Works with ChatInterface's built-in progress
Cons:
- No visual progress bar (just text status)
Option 2: Use show_progress="full" Parameter
Add explicit show_progress to ChatInterface and remove gr.Progress():
demo = gr.ChatInterface(
fn=research_agent,
title="🍆 DeepBoner",
show_progress="full", # Built-in spinner + runtime
...
)
Pros:
- Uses Gradio's intended mechanism
- Consistent with Gradio UX patterns
Cons:
- Less granular (no percentage, no custom desc)
Option 3: Custom Progress Component with CSS (COMPLEX)
Wrap ChatInterface in gr.Blocks and add a separate gr.HTML progress bar outside the chat:
with gr.Blocks() as demo:
progress_html = gr.HTML("<div id='custom-progress'></div>", visible=False)
chat = gr.ChatInterface(...)
# Update progress_html separately from chat
Pros:
- Full control over positioning
- Can match exact design requirements
Cons:
- Significant refactor required
- May break MCP server integration
- More moving parts = more bugs
Option 4: Hybrid - Progress in additional_inputs_accordion
Place a separate gr.Progress component in a fixed position (e.g., accordion header):
with gr.Blocks() as demo:
with gr.Row():
progress_bar = gr.Slider(0, 100, value=0, label="Progress", interactive=False)
chat = gr.ChatInterface(...)
Pros:
- Fixed position, no overlap
Cons:
- Requires
gr.Blockswrapper (breaks MCP?) - Clunky UX
Recommended Fix
Option 1: Remove gr.Progress(), keep emoji status text
Rationale:
- Our emoji status updates (
⏱️ **PROGRESS**: Round 2/5) already provide the information gr.Progress()was never designed for ChatInterface- Removing it eliminates the positioning conflict entirely
- ChatInterface's built-in
show_progress="minimal"handles the spinner
Implementation Plan
- Remove
progress: gr.Progress = gr.Progress()parameter fromresearch_agent() - Remove all
progress(...)calls in the function - Keep emoji status yields (
⏱️ **PROGRESS**: ...) - Optionally add
show_progress="minimal"to ChatInterface (already default) - Test on HuggingFace Spaces
Testing
# Local test
uv run python -c "from src.app import create_demo; demo, _ = create_demo(); demo.launch()"
# Verify:
# 1. No floating progress bar
# 2. Emoji status updates visible in chat
# 3. ChatInterface spinner works as expected
References
- Gradio Progress Bars Guide
- Gradio ChatInterface Docs
- GitHub Issue #5967: Progress bar for ChatInterface
- GitHub Issue #5815: Customize the progress bar
Version Info
- Gradio:
>=6.0.0(pyproject.toml constraint) - ChatInterface: Streaming async generator mode
- MCP Server: Enabled