Is it possible to display ipywidget within JupyterLab extension (react)

Hello Jupyter,

I’m trying to display an ipywidget within my JupyterLab extension (react).

The more I read about it and try, the less hopeful I become, as it seems quite complicated. My extension has access to the kernel and can communicate with it. Here’s what I found online:

The ipywidgets extension registers a factory for a notebook widget extension using the Document Registry. The createNew() function is called with a NotebookPanel and DocumentContext. The plugin then creates a ipywidget manager (which uses the context to interact the kernel and kernel’s comm manager). The plugin then registers an ipywidget renderer with the notebook instance’s rendermime (which is specific to that particular notebook).

When an ipywidget model is created in the kernel, a comm message is sent to the browser and handled by the ipywidget manager to create a browser-side ipywidget model. When the model is displayed in the kernel, a display_data output is sent to the browser with the ipywidget model id. The renderer registered in that notebook’s rendermime is asked to render the output. The renderer asks the ipywidget manager instance to render the corresponding model, which returns a JavaScript promise. The renderer creates a container lumino widget which it hands back synchronously to the OutputArea, and then fills the container with the rendered ipywidget when the promise resolves.
Source

This is the code I tried:

        const widgetContainerRef = useRef<HTMLDivElement>(null);
        const [widgetView, setWidgetView] = useState(null);
    
        useEffect(() => {
                const code = `
!pip install pygwalker
import pygwalker as pyg
import pandas as pd
    
df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
w = pyg.walk(df, return_widget=True)
display(w)
                `;
    
                try {
                    const future = context.sessionContext.session.kernel.requestExecute({ code: code });
    
                    future.onIOPub = (msg) => {
                        if (msg.header.msg_type === 'display_data') {
                            const content = msg.content;
                            if (content.data['application/vnd.jupyter.widget-view+json']) {
                                const widgetData = content.data['application/vnd.jupyter.widget-view+json'];
                                const modelId = widgetData.model_id;
                                renderWidget(modelId);
                            }
                        } else if (msg.header.msg_type === 'error') {
                            console.error(`Kernel Error: ${msg.content.ename}: ${msg.content.evalue}`);
                        }
                    };
    
                } catch (error) {
                    console.error("Error executing code:", error);
                }
            }
        }, [context.sessionContext.session.kernel]);
    
        const renderWidget = async (modelId) => {
            if (!context.rendermime) {
                console.error('Rendermime not available');
                return;
            }
    
            try {
                const model = await context.sessionContext.sessionManager.widgetManager.get_model(modelId);
                if (!model) {
                    console.error('Model not found');
                    return;
                }
    
                const renderer = context.rendermime.createRenderer('application/vnd.jupyter.widget-view+json');
                const widgetView = await renderer.renderModel({
                    data: {'application/vnd.jupyter.widget-view+json': {model_id: modelId}},
                    metadata: {}
                });
    
                setWidgetView(widgetView);
            } catch (error) {
                console.error('Error rendering widget:', error);
            }
        };
    
        useEffect(() => {
            if (widgetView && widgetContainerRef.current) {
                widgetContainerRef.current.innerHTML = '';
                widgetContainerRef.current.appendChild(widgetView.node);
            }
        }, [widgetView]);

But of course I get some errors, the comms doesn’t work:

I’m thinking there is probably an easier way; by reusing some classes from Jupyterlab used in the notebooks maybe. Or maybe it’s just too complicated. Any insights or tips welcome!

Thank you,
Thibaut

Nothing jumps at me. Can you create a minimal reproducer?

1 Like