Each session is assigned a unique application state by the Framework.

Initializing state

To set the initial application state, use the wf.init_state() method with a dictionary argument.

All user sessions will start with a clone of this initial state.

import writer as wf

# Define the initial state
initial_state = wf.init_state({
    "counter": 0,
})

# Define an event handler that modifies the state
# It receives the session state as an argument and mutates it
def increment(state):
    state["counter"] += 1

In the above example, each session begins with a counter at 0. As users interact with the application and activate event handlers, their session’s counter value will change. For instance, if a user triggers the increment handler three times, their counter will increase to 3.

To access the counter value in the Builder, use @.

Managing nested state elements

To include nested elements in your state, use nested dictionaries:

# Example of nested state initialization
wf.init_state({
    "counter": 0,
    "my_app": {
        "title": "Nested value"
    }
})

You can reference nested elements in the Builder as @{my_app.title}.

Backend-only state elements

By default, all of the elements in the session state are sent to the front-end.

All state elements are transmitted to the front-end by default, regardless of their visibility in the user interface.

To keep certain state elements private (back-end-only), prefix them with an underscore _. This is useful in several scenarios:

  1. When data synchronization to the front-end is unnecessary.
  2. When data cannot be serialized for the front-end, such as database connections.
  3. When data is sensitive to the specific session and should remain confidential.

These elements remain in the back-end and cannot be accessed from the Builder.

Managing files and binary data

In components where the Builder interfaces with external data, such as images, it often requires the use of data URLs. The source for an Image component, for example, can be a standard URL or a data URL.

Packing Files and Binary Data: Files and binary data can be converted to data URLs before they are sent to the front-end. Use wf.pack_file() and wf.pack_bytes() for this purpose. The mime_type argument, while optional, specifies the media type, helping the browser to correctly handle the data.

import writer as wf

# Initialize state with various data types
wf.init_state({
    # Reference a file by its filesystem path
    "sales_spreadsheet": wf.pack_file("sales_spreadsheet.xlsx"),

    # Use a file-like object that implements a .read() method
    "main_image": wf.pack_file(image_file, mime_type="image/jpeg"),

    # Convert raw bytes specifying a MIME type
    "my_bytes": wf.pack_bytes(b"\x31\x33\x33\x37", mime_type="text/plain"),

    # Directly assign raw bytes without a MIME type
    "my_raw_bytes": b"\x31\x33\x33\x37",
})

Handling non-standard data types

The front-end cannot directly display complex data types such as Pandas dataframes or Matplotlib figures. Such objects must be serialized before being sent.

Matplotlib figures are converted to PNG data URLs, which can be shown using a standard Image component.

wf.init_state({
    "my_matplotlib_fig": fig,
})

The element can be used in an Image component in the Builder by setting the source to @{my_matplotlib_fig}. Alternatively, as data inside a File Download component.

State schema

State schema is a feature that allows you to define the structure of the state. This is useful for ensuring that the state is always in the expected format.

Schema allows you to use features like

  • typing checking with mypy / ruff
  • autocomplete in IDEs
  • declare dictionaries
  • automatically calculate mutations on properties

more into Advanced > State schema

import writer as wf

class AppSchema(wf.WriterState):
    counter: int

initial_state = wf.init_state({
    "counter": 0
}, schema=AppSchema)

# Event handler
# It receives the session state as an argument and mutates it
def increment(state: AppSchema):
    state.counter += 1