Skip to content

Graph commands

On this page

When to use commands

Commands patch an existing browser graph without resending every element. Use them for progressive loading, CRUD, streaming updates, or large graphs. For small graphs, full elements_sync="always" reconciliation is simpler.

Each command has a non-empty scalar command_id and an operation. The browser applies a command ID once, which makes Streamlit reruns idempotent.

Mutation commands

Builder Operation Behavior
add_elements_command add_elements Adds only IDs not already present
upsert_elements_command upsert_elements Adds or updates by data.id
update_data_command update_data Merges or replaces one element's data
delete_elements_command delete_elements Removes selected IDs and optional incident edges
set_elements_command set_elements Replaces the full live graph
clear_graph_command clear Removes every live node and edge

“Browser command” means a JSON dictionary sent as a component prop and applied to the already mounted Cytoscape instance. It is not a shell command, network request, or JavaScript string.

Viewport commands

viewport_command() accepts fit, center, pan, zoom, set_viewport, set_zoom_bounds, and run_layout.

fit = viewport_command("fit-case", "fit", padding=60, duration=180)
zoom = viewport_command(
    "zoom-camera",
    "zoom",
    level=1.8,
    renderedPosition={"x": 480, "y": 260},
    duration=180,
)

Durations and padding are finite non-negative numbers. Pan dictionaries and zoom anchors require finite numeric x and y. Zoom bounds follow the same rules as component options.

Validation and application

validate_graph_commands(commands, elements=...) checks command shape, operations, IDs, endpoint references, update identity, delete safety, layout names, and viewport values.

apply_graph_command() and apply_graph_commands() return new Python elements dictionaries after applying mutation commands. Viewport commands leave Python elements unchanged.

Synchronization pattern

import streamlit as st

from st_graph_workbench import (
    apply_graph_command,
    graph_workbench,
    upsert_elements_command,
)

if "elements" not in st.session_state:
    st.session_state.elements = {"nodes": [], "edges": []}
if "commands" not in st.session_state:
    st.session_state.commands = []

if st.button("Load sighting"):
    command = upsert_elements_command(
        "load-sighting-1",
        nodes=[
            {"data": {"id": "ABC123", "label": "VEHICLE", "name": "ABC123"}},
            {"data": {"id": "gate-4", "label": "LOCATION", "name": "Gate 4"}},
        ],
        edges={
            "data": {
                "id": "ABC123-gate-4",
                "label": "SEEN_AT",
                "source": "ABC123",
                "target": "gate-4",
            }
        },
    )
    st.session_state.elements = apply_graph_command(st.session_state.elements, command)
    st.session_state.commands = [command]

graph_workbench(
    st.session_state.elements,
    graph_commands=st.session_state.commands,
    elements_sync="initial",
    key="command-driven-graph",
)

Always update Python state and the browser from the same command. Clear or replace the one-item command queue when a later action occurs, and never reuse a command ID for different content.

Conclusion

Commands optimize transport and preserve the live viewport. They do not remove the need for authoritative Python state or graph validation.