Repository navigation
Clientside Callbacks #266
Description
In Dash, changes to component properties in the front-end trigger user-supplied Python functions (@app.callback). This framework is very flexible: users have full control over the Python code that they write in their app.callback functions.
However, app.callback functions are simple. They filter some data or change the color of the chart or display some text. These data transformations, although simple, currently need to be processed entirely over the network in the Python backend. This makes the apps slower than they could be (because of the network delay) and less portable than they could be (because they require a running Python server).
Clientside Callbacks will introduce an interface for describing data transformation relationships between components in JavaScript. These data transformations will happen entirely in the client-side without passing data over the network. The Clientside callbacks Dash framework will enable developers to swap out their python-driven @app.callback functions with javascript-executed data transformations, enabling more performant apps.
As a quick background, here is what Dash Apps currently look like in Python:
The first part describes what the app looks like.
These classes just declaratively describe the names and the props of the React
components that they generate. These objects get serialized as JSON.
app.layout = html.Div([
html.H1('Example'),
comonents.TextInput(
id='my-text-input',
value='Initial value'
),
components.Dropdown(
id='my-dropdown',
options=[
'Option A', 'Option B', 'Option C'
]
value='Option A'
),
components.Graph(
id='my-graph'
figure={ # Just a plotly figure
'data': [...],
'layout': {...}
}
)
])The second part of dash apps describe the relationship between graphs.
This sets up "sources" ("inputs") and "sinks" ("outputs")
in the Dash front-end. Whenever any of the Input properties change, the
AJAX call gets made.
It's Reactive like a spreadsheet: as inputs change, the new values propagate
down the dependency graph, updating components in the correct order:
@callback(
Input(
id='my-dropdown',
property='value'
),
Input(
id='my-text-input',
property='value'
),
Output(
id='my-graph',
property='figure'
)
)
def update_graph(new_dropdown_value, new_text_input_value):
# compute a new figure based off of the new values of the dropdown
# or text input
return {
'data': [{
'x': [1, 2, 3],
'y': ({
'Option A': [3, 1, 2],
'Option B': [4, 3, 5],
'Option C': [1, 2, 4]
})[dropdown]
}],
'layout': {'title': text}
}These reactive updates happen entirely server-side through HTTP requests.
(This allows dash developers to do complicated updates or analytics through
their python context).
I think that we could extend this framework to work client-side as well.
Instead of a custom function defining how Inputs ("sources")
update Outputs ("sinks"), we could define a library of transformations components and a syntax for relating input properties to output properties. These transformations could be just be functional React components.
Here are some conceptual examples:

from dash.serverless import Selector as S
layout = Div([
Input(id='my-input', value='initial-value'),
H3(children=S('my-input', 'value'))
])In this example, we're setting the "children" property of the HTML H3 element
to just be the "value" of the "my-input" component. When "value" changes,
the content of the H3 element updates to match the new value.
I'm wrapping the ID and property with S to denote that the string represents a "reactive"
property corresponding to the component with the specified ID and that component's
property. (The actual API might be different, just using s for conceptual purposes.)
Now, consider a "Dataset" component and a "Graph":
layout = Div([
Dataset(
id='my-dataset'
columns={
'column-1': [1, 2, 3],
'column-2': [3, 1, 4]
},
column_names={
'column-1': 'My first column',
'column-2': 'My second column'
}
),
Graph(
figure={
'data': [{
'x': S('my-dataset', 'column-1'),
'y': S('my-dataset', 'column-2')
}]
}
)
])Note that not all components actually get rendered in the DOM. In this case,
the Dataset component isn't actually visible. It's just included as state.
If you wanted to view it as a table, it would look like:
layout = Div([
Dataset(
id='my-dataset'
columns={
'column-1': [1, 2, 3],
'column-2': [3, 1, 4]
},
column_names={
'column-1': 'My first column',
'column-2': 'My second column'
}
),
Table(data='::my-dataset.columns'),
Graph(
figure={
'data': [{
'x': S('my-dataset', 'columns', 'column-1'),
'y': S('my-dataset', 'columns', 'column-2')
}]
}
)
])You can imagine how there might be several datasets and several graphs in one
(like a dashboard or a report).
layout = Div([
Dataset(id='dataset-1', columns={...}),
Dataset(id='dataset-2', columns={...}),
Dataset(id='dataset-3', columns={...}),
Graph(id='graph-1',
data=[{'x': S('dataset-1', 'columns', 'column-1'), ...}]
),
Graph(id='graph-2',
data=[{'x': S('dataset-2', 'columns', 'column-1'), ...}]
),
Graph(id='graph-3',
data=[{'x': S('dataset-3, 'columns', 'column-1'), ...}]
)
])Now, we would also need a library for lightweight data transformations. I'm thinking something like Ramda.
import dash.clientside.transformations as T
import dash.clientside.selector as S
df = pd.DataFrame([
{'col-1': 1, 'col-2': 5, 'col-3': 10},
{'col-1': 2, 'col-2': 6, 'col-3': 11},
{'col-1': 3, 'col-2': 7, 'col-3': 12},
# ...
])
app.layout = html.Div([
# "Virtual" component that doesn't render anything
# to the screen, it just contains the data for other
# components to reference
dcc.Dataset(
id='my-dataset',
columns=df.columns,
rows=df.to_dict(),
),
# `Table` renders an actual table to the screen
dcc.Table(
rows=S('my-dataset', 'rows'),
columns=S('my-dataset', 'rows')
),
dcc.Graph(
figure={
# T.pluck('col-1', [{'col-1': 1}, {'col-1': 2}]) -> [1, 2]
'x': T.pluck(
'col-1',
S('my-dataset', 'rows')
),
'y': T.pluck(
'col-2',
S('my-dataset', 'rows')
)
}
)
])Or, extending this with dynamic components:
app.layout = html.Div([
dcc.Dataset(
id='my-dataset',
columns=df.columns,
rows=df.to_dict(),
),
dcc.Dropdown(
id='my-dropdown',
options=[
{'option': i, 'label': i}
for i in df.columns
],
value=df.columns[0]
),
dcc.Graph(
figure={
'x': T.pluck(
'col-1',
S('my-dataset', 'rows')
),
'y': T.pluck(
S('my-dropdown', 'value'),
S('my-dataset', 'rows')
)
}
)
])Here are some high-level architectural requirements and goals for this work item:
- The Dash Apps will still be created with Python
- The clientside callbacks will be executed in JavaScript
- The existing set of Dash components will be available (with the exception of network-connected components like the
mapboxcharts) - We will introduce a new syntax or language ("data transformation language") for declaratively describing the relationships and simple operations between component properties. This language will have a Python interface, will be serialized as JSON, and executed in JavaScript.
- This client-side data transformation will be available in server-connected Dash apps as well, enabling certain simple updates to happen quickly
- The app will not run arbitrary JavaScript, it will be designed in a way to be safe from XSS injections
- The "data transformation language" will operations like filtering, plucking values from nested objects, sorting, and arithmetic. It will draw inspiration from functional programming libraries and languages that enable concise, chainable, and immutable data transformations. See Ramda (http://ramdajs.com/) for an example.





Activity
ned2 commentedon Jun 17, 2018
This is super exciting, as it will greatly increase the range of contexts where Dash can be used. The plan to have serverless components be available within a server-connected app is a great idea. I've often found myself swapping bits of pre-computed layout in and out using callbacks. Having to do an entire round-trip just to change the page layout is not ideal. This will allow for much more responsive single-page apps.
valentijnnieman commentedon Jun 18, 2018
In theory, this would open up Dash apps to be used with different React renderers, such as these https://github.com/chentsulin/awesome-react-renderer right? Very exciting!
Tasselmi commentedon Jul 16, 2018
hey , guys. I think you make problems complicated.
Just look at plotly.offline.plot() . I think you should not create any new language at all . Just create a function or a package that can generate offline html files ,ok ? For example, we use dash as now and we view the app by visiting https://127.0.0.0:8000 or some address like this , a function u provide can download all the data and web UI and interactive features in the page and generate a offline html file . Studying a new language costs a lot time and the offline mode is quite different from now . I am afraid.
chriddyp commentedon Jul 16, 2018
Thanks for chiming in @Tasselmi !
This will be possible if you want to just export the
app.layoutof your app. Nothing new to learn here.However, if you want to convert your Python callbacks to a standalone file, we need some way to run those callbacks without a Python server running. So, that's what this "new language" is proposing: a way to write your Dash callbacks in a way that can be run without a Python server.
davidolmo commentedon Jul 26, 2018
I was looking for this feature before trying out Dash. This would be great
Jonnyb36 commentedon Aug 6, 2018
This would also be a really useful feature by itself as often I would just like to take a snapshot of the html with the existing widget states that have generated that html.
eruisi commentedon Aug 15, 2018
This is a feature missing from Dash compared to Bokeh autload_static mode. Very excited to see it will come to Dash too!
Do you have an ETA for the feature?
chriddyp commentedon Aug 15, 2018
Shooting for late fall, but I'll keep this post updated with progress.
Glad you're excited, it's going to be sweet :)
Tasselmi commentedon Aug 26, 2018
Hey guys. How is all of this going? I think offline mode is historic. As a data analyst, i will never use Microsoft office any more.
chriddyp commentedon Aug 26, 2018
That's what I'm talking about!
It's looking good. I promise to keep this thread updated with developments. All I can say now is that we're shooting for late fall.
Tasselmi commentedon Aug 26, 2018
Thank u for ur response.
Immortalin commentedon Oct 1, 2018
@chriddyp Easy way: https://github.com/QQuick/Transcrypt
Throw in pyodide if you want the Scipy kitchen sink. Having a custom DSL is nice yes, but having the full power of Python is even nicer. :D
14 remaining items
Btibert3 commentedon Apr 11, 2019
Any update on this?
chriddyp commentedon Apr 11, 2019
Done in #672
mungojam commentedon Apr 12, 2019
If anybody puts together a fully serverless dash app using this, it would be great to see an example. (I may try but can't guarantee that I'll get time for it)
vlizanae commentedon Apr 14, 2019
is there any kind of documentation for this already?
davidolmo commentedon Apr 26, 2019
I've seen the examples and it looks promising. If all callbacks are performed clientside, do you think there would be any way to export the Dash app to html+.js in order to allow offline usage?
louisdecharson commentedon May 20, 2019
Documentation
For those of you looking for an example, one can be found here:
https://community.plot.ly/t/dash-0-41-0-released/22131
mungojam commentedon May 21, 2019
I don't know about others, but I was asking for an example (if it's possible) of how to use this to generate a fully clientside app. I suspect it isn't possible at this stage, but would be good for the future as it enables things like s3 hosting of some basic dash apps. Though maybe we would gain very little in using dash over pure react at that point.
alexcjohnson commentedon May 21, 2019
Right, for now this feature is focused on performance, and does not provide a pathway to a fully serverless architecture.
mneira10 commentedon Jun 12, 2019
Does this let me get the corresponding html of a dash plot? I haven't been able to get the raw html yet and it is not clear to me how it can be done with this feature.
Has anyone been able to do this?
giiyms commentedon Jul 25, 2019
From this example did anyone figure out how to output to a single .html file?
Would love to pass Dash boards to colleagues without requiring server.
vantaka2 commentedon Aug 3, 2019
@chriddyp - sorry for tagging you on a closed issue! I have the same question as @mneira10 & @GuyMS above, seems like a few of us are looking for a way to export the html.
Higher up in this thread you mentioned this is possible
This will be possible if you want to just export the app.layout of your app. Nothing new to learn here.would you mind sharing a example of how to do this? Apologize if this is documented somewhere, I went though a lot of the documentation and have not found it yet.
Thanks again for all your great work!
I asked the same question on the dash community page: https://community.plot.ly/t/export-html-of-dash-app/26754 - I'll be sure to update everyone, if it gets answered or I figure it out.
chriddyp commentedon Aug 4, 2019
As mentioned above in #266 (comment), exporting to standalone HTML is not officially supported right now, this issue ended up focusing on the clientside callback architecture.