Skip to content

State Management

State management is a critical element of building interactive apps because it allows you store information about what the user did in a structured way.

Basic usage

You can register a class using the class decorator me.stateclass which is like a dataclass with special powers:

@me.stateclass
class State:
  val: str

You can get an instance of the state class inside any of your Mesop component functions by using me.state:

@me.page()
def page():
    state = me.state(State)
    me.text(state.val)

How State Works

me.stateclass is a class decorator which tells Mesop that this class can be retrieved using the me.state method, which will return the state instance for the current user session.

If you are familiar with the dependency injection pattern, Mesop's stateclass and state API is essentially a minimalist dependency injection system which scopes the state object to the lifetime of a user session.

Under the hood, Mesop is sending the state back and forth between the server and browser client so everything in a state class must be serializable.

Multiple state classes

You can use multiple classes to store state for the current user session.

Using different state classes for different pages or components can help make your app easier to maintain and more modular.

@me.stateclass
class PageAState:
    ...

@me.stateclass
class PageBState:
    ...

@me.page(path="/a")
def page_a():
    state = me.state(PageAState)
    ...

@me.page(path="/b")
def page_b():
    state = me.state(PageBState)
    ...

Under the hood, Mesop is managing state classes based on the identity (e.g. module name and class name) of the state class, which means that you could have two state classes named "State", but if they are in different modules, then they will be treated as separate state, which is what you would expect.

Nested State

You can also have classes inside of a state class as long as everything is serializable:

class NestedState:
  val: str

@me.stateclass
class State:
  nested: NestedState

def app():
  state = me.state(State)

Note: you only need to decorate the top-level state class with @me.stateclass. All the nested state classes will automatically be wrapped.

Nested State and dataclass

Sometimes, you may want to explicitly decorate the nested state class with dataclass because in the previous example, you couldn't directly instantiate NestedState.

If you wanted to use NestedState as a general dataclass, you can do the following:

@dataclass
class NestedState:
  val: str = ""

@me.stateclass
class State:
  nested: NestedState

def app():
  state = me.state(State)

Reminder: because dataclasses do not have default values, you will need to explicitly set default values, otherwise Mesop will not be able to instantiate an empty version of the class.

Now, if you have an event handler function, you can do the following:

def on_click(e):
    response = call_api()
    state = me.state(State)
    state.nested = NestedState(val=response.text)

If you didn't explicitly annotate NestedState as a dataclass, then you would get an error instantiating NestedState because there's no initializer defined.

Tips

Set mutable default values (e.g. list) correctly

Similar to regular dataclasses which disallow mutable default values, you need to avoid mutable default values such as list and dict for state classes. Allowing mutable default values could lead to erroneously sharing state across users which would be bad!

Bad: Setting a mutable field directly on a state class attribute.

@me.stateclass
class State:
  x: list[str] = ["a"]

Good: Use dataclasses field method to define a default factory so a new instance of the mutable value is created with each state class instance.

from dataclasses import field

@me.stateclass
class State:
  x: list[str] = field(default_factory=lambda: ["a"])

State performance issues

Because the state class is serialized and sent back and forth between the client and server, you should try to keep the state class reasonably sized. For example, if you store a very large string (e.g. base64-encoded image) in state, then it will degrade performance of your Mesop app. Instead, you should try to store large data outside of the state class (e.g. in-memory, filesystem, database, external service) and retrieve the data as needed for rendering.