Anywidget: Custom Jupyter Widgets Made Easy
raw JSON → 0.10.0 verified Sat Apr 25 auth: no python
Anywidget is a Python library that dramatically simplifies the process of creating custom interactive widgets for computational notebooks, running in environments like Jupyter, JupyterLab, Google Colab, VS Code, and Marimo. It's both a specification and a toolkit, allowing developers to define widget front-end code using standard ECMAScript modules (ESM) within a Python class. Currently at version 0.10.0, it features a rapid release cadence with frequent updates.
pip install "anywidget[dev]" Common errors
error ModuleNotFoundError: No module named 'anywidget' ↓
cause The 'anywidget' package is not installed in the Python environment.
fix
Install the package using pip: 'pip install anywidget'.
error ImportError: cannot import name 'AnyWidget' from 'anywidget' ↓
cause The 'AnyWidget' class is not available in the 'anywidget' module, possibly due to an incorrect import statement or version mismatch.
fix
Ensure you are using the correct import statement: 'from anywidget import AnyWidget'.
error Failed to load model class 'AnyModel' from module 'anywidget' Error: No version of module anywidget is registered ↓
cause The JavaScript code for 'anywidget' is not available, possibly due to installation issues or the need to reload the Jupyter environment.
fix
Reload the browser tab running Jupyter or ensure 'anywidget' is installed before launching Jupyter.
error Failed to load model class 'AnyModel' from module 'anywidget' Error: No version of module anywidget is registered. ↓
cause This error typically occurs when anywidget's JavaScript frontend code is not properly loaded or activated in the Jupyter environment, often happening if anywidget was installed after Jupyter was launched or if there's an environment mismatch (e.g., in VS Code).
fix
Restart your Jupyter server or reload the browser tab (not just the kernel). Ensure
anywidget is installed in the correct environment where Jupyter is running, and if using anywidget[dev], that your environment variables are correctly set for Hot Module Replacement (HMR). error Uncaught (in promise) Error: [anywidget] Failed to initialize model. ↓
cause This specific error arises due to an incompatibility between `anywidget` and `websockets` versions 16.0 or higher, affecting the WebSocket communication layer `anywidget` relies on.
fix
Pin your
websockets dependency to a version less than 16.0 in your requirements.txt or conda environment.yml, for example: websockets<16.0. Warnings
breaking Since v0.9, the preferred way to define front-end widget code has shifted to using lifecycle hooks (`initialize`, `render`) exported as a default object from the `_esm` module, replacing the direct `export function render(view)` pattern. ↓
fix Refactor your `_esm` JavaScript to `export default { initialize({ model }), render({ model, el }) { ... } };`. The `render` function's argument `view` is now split into `{ model, el }`.
breaking If you are using the Vite plugin for `anywidget`, the import path for it changed from `anywidget/vite` to `@anywidget/vite` to allow for independent versioning. ↓
fix Update your Vite configuration (e.g., `vite.config.mjs`) to `import anywidget from "@anywidget/vite";`.
gotcha Hot Module Replacement (HMR) for live development requires opting in by either setting the `ANYWIDGET_HMR` environment variable to `1` or, preferably, referencing frontend code via `pathlib.Path` for `_esm` and `_css` attributes. ↓
fix For in-notebook prototyping, define `_esm` and `_css` as `pathlib.Path('index.js')` (or similar) to external files, and ensure the `ANYWIDGET_HMR` environment variable is set if you expect HMR without explicit file paths.
gotcha The `__repr__` method for `AnyWidget` subclasses was overridden to provide a less verbose, `object.__repr__` like output, instead of the full serialization of all trait values inherited from `ipywidgets.Widget`. ↓
fix Be aware that inspecting `AnyWidget` instances directly might no longer show a full dump of all synchronized traits. For trait values, access them directly (e.g., `widget.count`).
gotcha Custom messages sent from Python (`widget.send()`) will only be received by the frontend if the widget's view has already been rendered (i.e., the widget has been `display()`ed). Sending messages before the widget is displayed will result in them being lost. ↓
fix Ensure that the `AnyWidget` instance is displayed in the notebook or environment before sending any custom messages from the Python kernel.
breaking For `anywidget` framework bridges like `@anywidget/react` and `@anywidget/svelte`, there have been breaking changes to align with newer versions of their respective frameworks (React 18's `useSyncExternalStore` and Svelte 5's runes reactivity). This primarily affects users building widgets with these specific frontend frameworks. ↓
fix Refer to the `anywidget` release notes and the specific framework bridge documentation for migration guides if you are using `@anywidget/react` or `@anywidget/svelte`.
Install
pip install anywidget conda install -c conda-forge anywidget Install compatibility last tested: 2026-04-25
runtime variant status import time mem disk
3.10-alpine dev 1.74s 28.3MB 63.2M
3.10-alpine default 1.81s 28.3MB 59.8M
3.10-slim dev 1.35s 28.3MB 64M
3.10-slim default 1.19s 28.3MB 60M
3.11-alpine dev 2.41s 32.6MB 68.0M
3.11-alpine default 2.44s 32.6MB 64.3M
3.11-slim dev 2.14s 32.6MB 68M
3.11-slim default 2.76s 32.6MB 65M
3.12-alpine dev 2.47s 31.7MB 59.1M
3.12-alpine default 2.43s 31.7MB 55.6M
3.12-slim dev 2.75s 31.7MB 59M
3.12-slim default 3.04s 31.7MB 56M
3.13-alpine dev 2.38s 33.3MB 58.8M
3.13-alpine default 2.21s 33.3MB 55.2M
3.13-slim dev 2.62s 33.3MB 59M
3.13-slim default 2.40s 33.3MB 56M
3.9-alpine dev 1.68s 28.6MB 62.2M
3.9-alpine default 1.61s 28.6MB 58.9M
3.9-slim dev 1.62s 28.6MB 63M
3.9-slim default 1.44s 28.6MB 60M
Imports
- AnyWidget
from anywidget import AnyWidget - Int
import traitlets class MyWidget(AnyWidget): value = traitlets.Int(0).tag(sync=True) - Unicode
import traitlets class MyWidget(AnyWidget): text = traitlets.Unicode('Hello').tag(sync=True)
Quickstart
import anywidget
import traitlets
from IPython.display import display
class CounterWidget(anywidget.AnyWidget):
_esm = """
export default {
initialize({ model }) {
// Optional: run once per widget instance
console.log('Widget initialized');
},
render({ model, el }) {
let getCount = () => model.get('count');
let button = document.createElement('button');
button.innerHTML = `count is ${getCount()}`;
button.addEventListener('click', () => {
model.set('count', getCount() + 1);
model.save_changes();
});
model.on('change:count', () => {
button.innerHTML = `count is ${getCount()}`;
});
el.appendChild(button);
return () => {
// Optional: cleanup on view removal
button.removeEventListener('click', () => {});
};
}
};
"""
_css = """ button { padding: 5px 10px; border-radius: 5px; background-color: #f0f0f0; } """
count = traitlets.Int(0).tag(sync=True)
widget = CounterWidget()
display(widget)
# You can also interact with the widget from Python
# widget.count = 5 # This will update the frontend