Skip to main content
When building Gradio applications, you often need to persist data between interactions. This is called state management. Gradio provides three ways to manage state:
  • Global state: Shared among all users, persists while the app is running
  • Session state: Unique to each user, persists during their session
  • Browser state: Stored in the browser’s localStorage, persists even after page refresh

Global state

Global state is straightforward: any variable created outside a function is shared between all users.
import gradio as gr

# This variable is shared between all users
visitor_count = 0

def increment_counter():
    global visitor_count
    visitor_count += 1
    return visitor_count

with gr.Blocks() as demo:
    number = gr.Textbox(label="Total Visitors", value="Counting...")
    demo.load(increment_counter, inputs=None, outputs=number)

demo.launch()
Every user who opens the app increases the visitor count. The value is shared globally across all sessions.
When not to use global state: If you need values that are unique to each user (like chat history, user preferences, or individual progress), use session state or browser state instead.

Session state

Session state allows you to persist data for each user individually. Data is maintained across multiple interactions but does not transfer between users. If a user refreshes the page, their session state is reset. To use session state, follow these steps:
1

Create a gr.State object

Initialize it with a default value if needed. The value must be deepcopy-able.
cart = gr.State([])
2

Add State as input/output

Include the State object in your event listener’s inputs and outputs as needed.
button.click(fn=add_items, inputs=[items, cart], outputs=cart)
3

Use State in your function

Add the state variable to your function’s parameters and return value.
def add_items(new_items, previous_cart):
    cart = previous_cart + new_items
    return cart

Example: Shopping cart

Here’s a complete example of a simple shopping cart:
import gradio as gr

with gr.Blocks() as demo:
    cart = gr.State([])  # Initialize empty cart
    items_to_add = gr.CheckboxGroup(
        ["Cereal", "Milk", "Orange Juice", "Water"]
    )
    
    def add_items(new_items, previous_cart):
        cart = previous_cart + new_items
        return cart
    
    gr.Button("Add Items").click(
        add_items, 
        [items_to_add, cart], 
        cart
    )
    
    cart_size = gr.Number(label="Cart Size")
    cart.change(lambda cart: len(cart), cart, cart_size)

demo.launch()
Think of gr.State as an invisible Gradio component that can store any value. In this example, cart is not visible in the UI but is used for calculations.

Understanding State changes

The .change() listener for a State variable triggers after any event listener modifies the state:
  • For sequences (list, set, dict): Triggers if any elements change
  • For objects or primitives: Triggers if the hash of the value changes
If you create a custom class and use it with gr.State, make sure the class includes a sensible __hash__ implementation.

State persistence

Session state values:
  • Are cleared when the user refreshes the page
  • Are stored in the app backend for 60 minutes after the user closes the tab
  • Can have custom retention with the delete_cache parameter in gr.Blocks
with gr.Blocks(delete_cache=(3600, 3600)) as demo:
    # Delete cached state every hour (3600 seconds)
    pass
Learn more about State in the State documentation.

Working with non-deepcopyable objects

Some objects cannot be deepcopied (like threading locks or database connections). For these, use a global dictionary keyed by session hash:
import gradio as gr

class NonDeepCopyable:
    def __init__(self):
        from threading import Lock
        self.counter = 0
        self.lock = Lock()  # Lock objects cannot be deepcopied
    
    def increment(self):
        with self.lock:
            self.counter += 1
            return self.counter

# Global dictionary to store user-specific instances
instances = {}

def initialize_instance(request: gr.Request):
    instances[request.session_hash] = NonDeepCopyable()
    return "Session initialized!"

def cleanup_instance(request: gr.Request):
    if request.session_hash in instances:
        del instances[request.session_hash]

def increment_counter(request: gr.Request):
    if request.session_hash in instances:
        instance = instances[request.session_hash]
        return instance.increment()
    return "Error: Session not initialized"

with gr.Blocks() as demo:
    output = gr.Textbox(label="Status")
    counter = gr.Number(label="Counter Value")
    increment_btn = gr.Button("Increment Counter")
    
    increment_btn.click(increment_counter, inputs=None, outputs=counter)
    
    # Initialize instance when page loads
    demo.load(initialize_instance, inputs=None, outputs=output)
    
    # Clean up instance when page is closed/refreshed
    demo.unload(cleanup_instance)

demo.launch()
1

Create a global dictionary

Store one instance per user, keyed by their session_hash.
2

Initialize on load

Use demo.load() to create an instance when the user first visits.
3

Clean up on unload

Use demo.unload() to delete the instance when the user leaves.
4

Access via session_hash

Use gr.Request to get the user’s session_hash in your functions.

Browser state

Browser state persists data in the browser’s localStorage, allowing it to survive page refreshes and even browser restarts. This is useful for storing user preferences, API keys, or settings.
1

Create a gr.BrowserState object

Optionally provide a default value and a storage key.
username = gr.BrowserState("")
2

Use like gr.State

Add it to event listeners as inputs and outputs.
button.click(save_username, inputs=username, outputs=status)

Example: Persisting login credentials

import gradio as gr

def save_credentials(username, password):
    # In a real app, you would validate and hash the password
    return username, password, "Credentials saved!"

def load_status(username):
    if username:
        return f"Welcome back, {username}!"
    return "No saved credentials found."

with gr.Blocks() as demo:
    gr.Markdown("# Login Demo")
    
    # Store credentials in browser localStorage
    stored_username = gr.BrowserState("")
    stored_password = gr.BrowserState("")
    
    status = gr.Textbox(label="Status", interactive=False)
    
    with gr.Row():
        username_input = gr.Textbox(label="Username")
        password_input = gr.Textbox(label="Password", type="password")
    
    save_btn = gr.Button("Save Credentials")
    save_btn.click(
        save_credentials,
        inputs=[username_input, password_input],
        outputs=[stored_username, stored_password, status]
    )
    
    # Load saved credentials on page load
    demo.load(load_status, inputs=stored_username, outputs=status)

demo.launch()

BrowserState persistence

Important: Data stored in gr.BrowserState does not persist if the Gradio app is restarted, unless you:
  1. Hardcode specific values for storage_key and secret in gr.BrowserState
  2. Restart the Gradio app on the same server name and port
Only do this if you’re running trusted Gradio apps, as different apps on the same domain could access the same localStorage data.

Choosing the right state type

State TypeShared Between UsersPersists on RefreshPersists After Browser CloseUse Cases
Global State✅ Yes✅ Yes (until app restarts)✅ Yes (until app restarts)Visitor counters, global settings, shared resources
Session State❌ No❌ No❌ NoChat history, form data, user progress
Browser State❌ No✅ Yes✅ YesUser preferences, API keys, login credentials

Next steps

Now that you understand state management, you can: