State management allows your Gradio applications to remember information between user interactions. This is essential for building applications that need to track conversation history, maintain user sessions, or accumulate data over multiple steps.
What is state?
In Gradio, “state” refers to data that persists across multiple function calls within a user session. Without state, each interaction is independent - the application has no memory of previous inputs or outputs.
State in Gradio is:
- Session-specific: Each user has their own isolated state
- Temporary: State resets when the user refreshes the page or their session expires
- Invisible: State components don’t appear in the UI
- Flexible: Can store any Python object (lists, dicts, custom objects)
The State component
The simplest way to manage state is with the State component:
import gradio as gr
def increment(count):
count = count + 1
return count, f"Count: {count}"
with gr.Blocks() as demo:
state = gr.State(value=0) # Initialize state to 0
output = gr.Textbox(label="Current Count")
button = gr.Button("Increment")
button.click(
fn=increment,
inputs=state, # State is an input
outputs=[state, output] # State is also an output
)
demo.launch()
Key points:
- Initialize state with a default value:
gr.State(value=0)
- Include state in both
inputs and outputs to update it
- Return the updated state from your function
Common state patterns
Accumulating chat history
Maintain conversation context in a chatbot:
import gradio as gr
import random
import time
def respond(message, chat_history):
bot_message = random.choice(["How are you?", "Nice day!", "Interesting!"])
chat_history.append({"role": "user", "content": message})
chat_history.append({"role": "assistant", "content": bot_message})
time.sleep(1)
return "", chat_history
with gr.Blocks() as demo:
chatbot = gr.Chatbot()
msg = gr.Textbox(placeholder="Type a message...")
clear = gr.ClearButton([msg, chatbot])
msg.submit(respond, [msg, chatbot], [msg, chatbot])
demo.launch()
The chatbot component itself acts as state, storing the message history.
Multi-step workflows
Collect information across multiple interactions:
import gradio as gr
def collect_name(name, data):
data = data or {}
data["name"] = name
return data, f"Hello {name}! Now enter your age."
def collect_age(age, data):
data["age"] = age
return data, f"Thanks! You are {age} years old."
with gr.Blocks() as demo:
state = gr.State(value={})
output = gr.Textbox(label="Status")
name_input = gr.Textbox(label="Name")
name_btn = gr.Button("Submit Name")
age_input = gr.Number(label="Age")
age_btn = gr.Button("Submit Age")
name_btn.click(collect_name, [name_input, state], [state, output])
age_btn.click(collect_age, [age_input, state], [state, output])
demo.launch()
Maintaining counters
Track user actions or iterations:
import gradio as gr
def track_clicks(click_count, click_history):
click_count += 1
click_history.append(f"Click #{click_count}")
return click_count, click_history, "\n".join(click_history)
with gr.Blocks() as demo:
click_count = gr.State(value=0)
click_history = gr.State(value=[])
output = gr.Textbox(label="Click History", lines=5)
button = gr.Button("Click Me!")
button.click(
fn=track_clicks,
inputs=[click_count, click_history],
outputs=[click_count, click_history, output]
)
demo.launch()
State in Interface
When using gr.Interface, you can use the special "state" string shortcut:
import gradio as gr
def process_with_state(text, state):
state = state or []
state.append(text)
history = "\n".join(state)
return history, state
demo = gr.Interface(
fn=process_with_state,
inputs=["textbox", "state"],
outputs=["textbox", "state"]
)
demo.launch()
When using Interface with state, there must be exactly one state input and one state output.
Complex state objects
State can hold any Python object, including custom classes:
import gradio as gr
from dataclasses import dataclass
from typing import List
@dataclass
class UserSession:
name: str = ""
messages: List[str] = None
preferences: dict = None
def __post_init__(self):
if self.messages is None:
self.messages = []
if self.preferences is None:
self.preferences = {}
def initialize():
return UserSession(), "Session initialized"
def add_message(message, session):
session.messages.append(message)
count = len(session.messages)
return session, f"Total messages: {count}"
with gr.Blocks() as demo:
session = gr.State(value=UserSession())
status = gr.Textbox(label="Status")
init_btn = gr.Button("Initialize Session")
init_btn.click(initialize, None, [session, status])
msg_input = gr.Textbox(label="Message")
msg_btn = gr.Button("Add Message")
msg_btn.click(add_message, [msg_input, session], [session, status])
demo.launch()
State vs component values
When to use State
Use gr.State when you need to:
- Store data that shouldn’t be visible to users
- Maintain large data structures (models, datasets)
- Track internal application state
- Keep intermediate computation results
import gradio as gr
# Good use of State: internal counter
with gr.Blocks() as demo:
counter = gr.State(value=0) # Not visible
display = gr.Number(label="Count") # Visible
btn = gr.Button("Increment")
btn.click(
lambda c: (c + 1, c + 1),
counter,
[counter, display]
)
When to use regular components
Use regular components as “state” when:
- Users need to see the persisted value
- The state is simple and fits a component type
- You want users to potentially edit the state
import gradio as gr
# Good use of visible component as state: editable history
with gr.Blocks() as demo:
history = gr.Textbox(label="History", lines=10)
new_entry = gr.Textbox(label="Add Entry")
btn = gr.Button("Add")
btn.click(
lambda entry, hist: hist + "\n" + entry,
[new_entry, history],
history
)
Initializing state
Default values
Set initial state when creating the component:
import gradio as gr
with gr.Blocks() as demo:
# Simple types
counter = gr.State(value=0)
text_state = gr.State(value="")
list_state = gr.State(value=[])
dict_state = gr.State(value={})
# Complex objects
custom_state = gr.State(value={"user": "Alice", "score": 100})
Dynamic initialization
Initialize state based on computations:
import gradio as gr
import time
def init_with_timestamp():
return {"started_at": time.time(), "actions": []}
with gr.Blocks() as demo:
session = gr.State(value=None)
status = gr.Textbox()
demo.load(
lambda: (init_with_timestamp(), "Session started"),
None,
[session, status]
)
Clearing state
Reset state to initial values:
import gradio as gr
def reset():
return 0, "", "State cleared"
with gr.Blocks() as demo:
counter = gr.State(value=0)
messages = gr.Textbox(label="Messages")
status = gr.Textbox(label="Status")
increment_btn = gr.Button("Increment")
reset_btn = gr.Button("Reset")
increment_btn.click(
lambda c: (c + 1, f"Count: {c + 1}", ""),
counter,
[counter, messages, status]
)
reset_btn.click(
reset,
None,
[counter, messages, status]
)
Or use the ClearButton:
import gradio as gr
with gr.Blocks() as demo:
state = gr.State(value=[])
textbox = gr.Textbox()
# Clears both state and textbox to their default values
clear_btn = gr.ClearButton([state, textbox])
State limitations
Session isolation
Each user session has independent state:
import gradio as gr
# This counter is per-user, not global
with gr.Blocks() as demo:
counter = gr.State(value=0)
output = gr.Number()
btn = gr.Button("Increment")
btn.click(
lambda c: (c + 1, c + 1),
counter,
[counter, output]
)
User A clicking “Increment” doesn’t affect User B’s counter.
If you need to share data across all users, use global Python variables or a database. However, be careful with thread safety!
Not persistent across refreshes
State resets when the page refreshes:
import gradio as gr
# This counter resets to 0 when user refreshes the page
with gr.Blocks() as demo:
counter = gr.State(value=0)
# User increments to 10, refreshes page → back to 0
For true persistence, use external storage (databases, files, etc.).
Memory considerations
State is stored in server memory:
import gradio as gr
# Careful! Large state uses server memory
with gr.Blocks() as demo:
# This could use a lot of memory with many users
large_data = gr.State(value=[0] * 1000000)
Avoiding storing very large objects in State, especially with many concurrent users. Consider using file storage or databases for large data.
Advanced patterns
Conditional state updates
Update state only under certain conditions:
import gradio as gr
def conditional_update(value, threshold, state):
if value > threshold:
state.append(value)
return state, f"Added {value} to state"
return state, f"Value {value} too low"
with gr.Blocks() as demo:
state = gr.State(value=[])
value_input = gr.Number(label="Value")
threshold = gr.Number(label="Threshold", value=10)
status = gr.Textbox(label="Status")
btn = gr.Button("Check and Add")
btn.click(
conditional_update,
[value_input, threshold, state],
[state, status]
)
State with multiple functions
Pass state through a chain of operations:
import gradio as gr
def step1(input, state):
state["step1_result"] = input.upper()
return state, state["step1_result"]
def step2(state):
result = state["step1_result"] + "!!!"
state["step2_result"] = result
return state, result
with gr.Blocks() as demo:
state = gr.State(value={})
inp = gr.Textbox(label="Input")
mid = gr.Textbox(label="After Step 1")
out = gr.Textbox(label="After Step 2")
btn = gr.Button("Process")
btn.click(
step1, [inp, state], [state, mid]
).then(
step2, state, [state, out]
)
See also
- Components - State is a special component
- Events - How to update state via events
- Blocks - Using state in custom layouts
- ChatInterface - Built-in chat history state management