React#

Many JupyterLab APIs require Lumino Widgets which have some additional features over native DOM elements, including:

  • Resize events that propagate down the Widget hierarchy.

  • Lifecycle events (onBeforeDetach, onAfterAttach, etc.).

  • Both CSS-based and absolutely positioned layouts.

We support wrapping React components to turn them into Lumino widgets using the ReactWidget class from @jupyterlab/ui-components:

/*
 * Copyright (c) Jupyter Development Team.
 * Distributed under the terms of the Modified BSD License.
 */

import * as React from 'react';

import { Widget } from '@lumino/widgets';
import { ReactWidget } from '@jupyterlab/ui-components';

function MyComponent() {
  return <div>My Widget</div>;
}

const myWidget: Widget = ReactWidget.create(<MyComponent />);
Widget.attach(myWidget, document.body);

Here we use the create static method to transform a React element into a Lumino widget. Whenever the widget is mounted, the React element will be rendered on the page.

If you need to handle other life cycle events on the Lumino widget or add other methods to it, you can subclass ReactWidget and override the render method to return a React element:

/*
 * Copyright (c) Jupyter Development Team.
 * Distributed under the terms of the Modified BSD License.
 */

import * as React from 'react';

import { Widget } from '@lumino/widgets';
import { ReactWidget } from '@jupyterlab/ui-components';

function MyComponent() {
  return <div>My Widget</div>;
}
class MyWidget extends ReactWidget {
  render() {
    return <MyComponent />;
  }
}
const myWidget: Widget = new MyWidget();
Widget.attach(myWidget, document.body);

We use Lumino Signals to represent data that changes over time in JupyterLab. To have your React element change in response to a signal event, use the UseSignal component from @jupyterlab/ui-components, which implements the “render props”:

/*
 * Copyright (c) Jupyter Development Team.
 * Distributed under the terms of the Modified BSD License.
 */

import * as React from 'react';

import { ReactWidget, UseSignal } from '@jupyterlab/ui-components';

import { ISignal, Signal } from '@lumino/signaling';

import { Widget } from '@lumino/widgets';

function MyComponent() {
  return <div>My Widget</div>;
}

function UseSignalComponent(props: { signal: ISignal<MyWidget, void> }) {
  return <UseSignal signal={props.signal}>{() => <MyComponent />}</UseSignal>;
}

class MyWidget extends ReactWidget {
  render() {
    return <UseSignalComponent signal={this._signal} />;
  }

  private _signal = new Signal<this, void>(this);
}

const myWidget: Widget = new MyWidget();
Widget.attach(myWidget, document.body);

The running component and the createSearchOverlay function in the search overlay use both of these features and serve as a good reference for best practices.

There is also a simple example making a Lumino react widget in the JupyterLab extension examples repository.

We currently do not have a way of embedding Lumino widgets inside of React components. If you find yourself trying to do this, we would recommend either converting the Lumino widget to a React component or using a Lumino widget for the outer layer component.

We follow the React documentation and “React & Redux in TypeScript - Static Typing Guide” for best practices on using React in TypeScript.