I wanted to share a small library I built for Dash apps with highly dynamic layouts: liquid-dash.
The problem it is trying to address is the callback churn you can get with pattern-matching callbacks in dynamic UIs. With MATCH or ALL, the callback depends directly on UI elements that are created and removed dynamically. So when new matching components enter the layout, the callback can run because the input set changed, even though the user interaction you care about did not happen.
The idea in liquid-dash is to move the callback dependency off of those dynamic controls and onto one fixed event source.
Instead of wiring dynamic buttons directly into the callback graph, each button carries metadata describing the interaction, and a delegated frontend handler routes that interaction into a stable event bridge. That bridge lives outside the subtree that gets rebuilt.
That “outside the subtree” part is important. The goal is for the callback to depend on a component that remains in place while the dynamic region changes. If the bridge sits inside the region being replaced, it becomes part of the layout churn rather than a fixed event source.
A simplified example looks like this:
html.Div([
EventBridge("main-events"),
DynamicRegion(
bridge="main-events",
children=[
action_button(
"Delete row",
action="delete_row",
target="row-17",
payload={"row_id": 17},
)
],
),
])
Here the metadata does two jobs:
bridgetells the frontend handler which stable store should receive the eventaction,target, andpayloadtell the callback what happened and what it applies to
So the button itself can be dynamic, but the callback input is not which prevents those spurious clicks. The UI emits intent through a stable bridge, the app updates state from that event, and the interface rerenders from state.
I’m sure the general pattern is not new. What this library tries to do is make the semantics explicit enough that the event flow is easy to reason about and hard to screw up ![]()