Developing with JavaScript (CustomJS)
When you need behavior that Bokeh's Python callbacks don't cover - or you want logic to run entirely in the browser - you embed JavaScript. CustomJS is the model for client-side callbacks: you pass a JS string that receives the Bokeh document and the event, and it manipulates the plot's JS-side state directly.
CustomJS Basics
from bokeh.models import CustomJS, Button
from bokeh.plotting import figure
p = figure()
p.circle([1, 2, 3], [1, 2, 3])
js = '''
var circles = cb_obj.ownerDocument.select_one("circle").data;
circles.x = [3, 2, 1];
circles.y = [3, 2, 1];
cb_obj.ownerDocument.invalidate();
'''
btn = Button(label="Flip", js_on_change={"click": CustomJS(code=js)})
Passing Python Values into JS
from bokeh.models import CustomJS, Slider
slider = Slider(start=0, end=10, value=5)
js = '''
var threshold = args.threshold;
var src = cb_obj.ownerDocument.get_model_by_id(args.src_id);
// filter src.data based on threshold ...
'''
slider.js_on_change("value", CustomJS(args={"threshold": 5, "src_id": "..."},
code=js))
args maps Python values (or model references) into the JS scope as args.name.
js_on_change on Any Model
from bokeh.models import CustomJS
source.js_on_change("data", CustomJS(code='''
console.log("data changed", cb_obj.data);
'''))
When to Reach for JS
- Client-only logic that must run without a server (static HTML exports).
- Accessing browser APIs (localStorage, fetch to your own backend).
- Complex visual effects or DOM manipulation Bokeh models don't expose.
- Keep it minimal: prefer Python callbacks and Bokeh models when possible - they are easier to maintain and work in server apps.
Summary
Embed JavaScript in Bokeh with CustomJS: attach it via js_on_change or js_on_click, receive the event through cb_obj, reach the document and models via cb_obj.ownerDocument, and pass Python values through the args dict. Use JS for browser-only behavior and custom effects; prefer Python callbacks and data models for everything else.