-
Allow
initializeto return an exports object (#974)initializecan now return a plain object to expose a programmatic API for the widget. This API is accessible to parent widgets viahost.getWidget().export default { initialize({ model }) { return { getValue: () => model.get("value"), setValue: (v) => { model.set("value", v); model.save_changes(); }, }; }, render({ model, el }) { /* ... */ }, };
The return type is distinguished by
typeof: functions are treated as cleanup callbacks (existing behavior), objects are treated as exports, andvoidmeans neither. -
Add
signal(AbortSignal) toinitializeandrenderprops for lifecycle cleanup (#974)Both
initializeandrendernow receive anAbortSignalvia thesignalprop. The signal is aborted when the widget is destroyed (or during HMR). This is the preferred way to manage cleanup going forward — it composes with the broader web platform (addEventListener,fetch, child widgets) and avoids the need to manually track teardown logic.The previous callback-based pattern continues to work but is no longer recommended:
// before export default { render({ model, el }) { let handler = () => { /* ... */ }; model.on("change:value", handler); return () => model.off("change:value", handler); }, }; // after export default { render({ model, el, signal }) { let handler = () => { /* ... */ }; model.on("change:value", handler); signal.addEventListener("abort", () => model.off("change:value", handler)); }, };
signalalso works withaddEventListenerandfetchdirectly:export default { render({ model, el, signal }) { el.addEventListener( "click", () => { /* ... */ }, { signal } ); }, };
-
Add
host.getWidgetandhost.getModelfor widget composition (#974)rendernow receives ahostprop with methods to resolve child widgets by reference, enabling one anywidget to render another inside its DOM.export default { async render({ model, el, signal, host }) { let child = await host.getWidget(model.get("slider")); await child.render({ el, signal }); }, };
On the Python side, widget references are serialized as
"anywidget:<model_id>"strings. A newWidgetTraittraitlet validates anywidget-compatible objects, and aWidgettype alias is provided for annotations:import anywidget class Dashboard(anywidget.AnyWidget): _esm = "dashboard.js" slider = anywidget.WidgetTrait().tag(sync=True) Dashboard(slider=Slider())
host.getWidget(ref)returns{ exports, render }whereexportsis the object returned from the child'sinitialize, andrender({ el, signal })mounts the child's view.host.getModel(ref)returns the raw model for lower-level access.
-
Clarify
change:event callback signature as() => void(#938)The
on("change:...")callback now takes no arguments. Usemodel.get()inside the callback to read the current value. The previous signature with(_: unknown, value: Payload)leaked Backbone.js implementation details from ipywidgets that are not portable across host platforms.
-
Makes explicit WidgetManager interface (#670)
Drops
@jupyter-widgets/baseas a dependency and instead makes an explicit interface forAnyModel.widget_manager. Right now we only supportwidget_manager.get_model, so having the other methods on the interface was misleading (leading to issues around.create_viewnot being supported).
-
experimental Replace invoke timeout with more flexible
AbortSignal(#540)This allows more flexible control over aborting the invoke request, including delegating to third-party libraries that manage cancellation.
export default { async render({ model, el }) { const controller = new AbortController(); // Randomly abort the request after 1 second setTimeout(() => Math.random() < 0.5 && controller.abort(), 1000); const signal = controller.signal; model .invoke("echo", "Hello, world", { signal }) .then((result) => { el.innerHTML = result; }) .catch((err) => { el.innerHTML = `Error: ${err.message}`; }); }, };
- Export
Experimentaltype (#524)
-
Add experimental
invokeAPI to call Python functions from the front end and (#453) await the response.This removes a lot of boilerplate required for this pattern. The API is experimental and opt-in only. Subclasses must use the
commandto register functions.class Widget(anywidget.AnyWidget): _esm = """ export default { async render({ model, el, experimental }) { let [msg, buffers] = await experimental.invoke("_echo", "hello, world"); console.log(msg); // "HELLO, WORLD" }, }; """ @anywidget.experimental.command def _echo(self, msg, buffers): # upper case the message return msg.upper(), buffers
- Add
AnyWidgetdefinition (9aa8dcc8558e00e33fbe4506b68ae30113df3728)
- Add
Initializemethod types (#395)
- feat: expose the
IWidgetManagerfrom@jupyter-widgets/baseto render function. (f2dbdbf)
- feat: Infer event payloads from model (
272782b)
-
feat: Autocomplete event names for known model events (#151)
/** * @typedef Model * @prop {number} value - the current count */ /** @type {import("@anywidget/types").Render<Model>} */ export function render({ model, el }) { model.on("change:value", () => { /* ... */); // ^ auto-completed in editor }
- fix: re-expose model.send for custom messages (#146)
-
feat: restrict backbone model access in render context (#140)
-
feat!: Limit view fields exposed to render function (#138)
BREAKING: The render function's argument has been refactored from a full
AnyViewto a simple object. This object only exposes themodelandelfields to the user-providedrenderfunction. This change aims to simplify the API and reduce potential misuse. Please ensure your render function only depends on these fields.