Runs clientside callback functions in Dash applications using Python syntax, eliminating the need for inline JavaScript.

As a Dash developer, I wanted a way to write clean, maintainable code without constantly switching between Python and JavaScript. Writing clientside logic inside a string felt clunky, and I knew there had to be a way to take advantage of quick clientside features of Dash without sacrificing the readability and syntax-highlighting of my code.

The Problem with Dash’s Clientside Callbacks

Dash’s clientside_callback requires you to write JavaScript inline within a Python string, which is cumbersome for several reasons:

  • Poor Readability: JavaScript inside a Python string isn’t easy to read or debug.
  • Lack of Syntax Highlighting: Writing JavaScript inside Python means missing out on proper IDE support.
  • Feels Inelegant: Writing code inside a string doesn’t feel as structured or natural as writing functions normally.

I wanted a better way to keep my Dash applications maintainable and easy to develop.

My Solution: better-dash-callback

With better-dash-callback, you can now write clientside callback functions in Python syntax. The library then translates your Python functions into JavaScript using metapensiero.pj, a Python-to-JavaScript transpiler. This keeps the workflow seamless and intuitive while improving maintainability.

How to use

To start using better-dash-callback, just install it via pip and start using it in your Plotly Dash projects.

1
pip3 install better-dash-callback

Now you can replace your awkward-looking JavaScript clientside callbacks with Python syntax.

Let’s compare how the Javascript clientside callbacks look with better-dash-callback:

1
2
3
4
5
6
7
8
9
10
11
from dash import clientside_callback

clientside_callback(
"""
function(value) {
return 'You entered: ' + value;
}
""",
Output("output", "children"),
Input("input", "value")
)
1
2
3
4
5
6
7
8
9
from better_dash_callback import callback

@callback(
Output("output", "children"),
Input("input", "value"),
clientside=True
)
def update_output(value):
return f"You entered: {value}"

As you can see above, the better-dash-callback version is more readable, Pythonic, and avoids the hassle of mixing JavaScript syntax inside Python. It’s satisfying to write clientside and serverside callbacks using the same format, with the same python syntax highlighting, with the only difference being that clientside callbacks have clientside=True in the decorator’s arguments.

How It Works

The callback function I implemented works just like Dash’s standard @callback decorator but with an added clientside argument. If clientside=True, the function’s Python source code is extracted and passed to metapensiero.pj, which translates it to JavaScript before Dash registers it as a clientside callback.

print() statements are translated to console.log() statements, and other python syntax is translated to the equivalent javascript syntax. You can access any existing javascript objects as if they are python objects. For example, you can access the window object as if it were a python object. For example, returning no_update is the same in the clientside python version as it is in javascript: return window.dash_clientside.no_update.

I also included some additional options that are offered by the metapensiero.pj library:

  • disable_es6=True: Disables ES6 syntax for broader browser compatibility.
  • enable_stage3=True: Enables Stage 3 JavaScript syntax for more advanced features.

For more information on the syntax that metapensiero.pj supports for translating Python to Javascript, check out metapensiero.pj’s GitHub.

How I created better-dash-callback

This is the source code for better-dash-callback.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import metapensiero.pj.__main__, dash, inspect

def callback(*args, clientside=False, disable_es6=False, enable_stage3=False, **kwargs):
def decorator(func):
if clientside:
python_code = inspect.getsource(func)
python_code = python_code[python_code.find("def "):]
js_code = metapensiero.pj.__main__.transform_string(python_code, enable_es6=not disable_es6, enable_stage3=enable_stage3)
dash.clientside_callback(js_code, *args, **kwargs)
else:
@dash.callback(*args, **kwargs)
def wrapper(*values):
return func(*values)
return decorator

This will essentially pass through the *args and *kwargs directly to @dash.callback if clientside is not set to True.

1
2
3
4
5
...
@dash.callback(*args, **kwargs)
def wrapper(*values):
return func(*values)
...

Otherwise, when clientside IS set to True, better-dash-callback will perform these steps:

  1. inspect to get the source code of the callback function
  2. Parsing out callback function definition by starting at first instance of def, (since inspect was also pulling in the source code of the decorator statement too)
  3. Transforming string to javascript using metapensiero.pj.__main__.transform_string
  4. Passing the generated javascript code into dash.clientside_callback

Why I Built This

While developing Dash applications, I often found writing JavaScript inside Python strings to be clunky. It lacked syntax highlighting, made debugging more difficult, and felt like there had to be a more elegant way. That’s why I created better-dash-callback, a Python library that lets you write clientside callbacks using Python syntax while seamlessly converting them to JavaScript under the hood.

better-dash-callback makes it easier to work with clientside callbacks, improves development speed, and keeps Dash applications more readable.

I’d love to hear your feedback! Feel free to check out the GitHub repo and try it out in your own projects. Let me know how it works for you!

Get new posts by email:

Comments

Hint: To type code with Disqus, use
<code><pre>insert.code.here()<pre/><code/>