Creating a React/Redux JupyterLab Extension

Davis E Ford
Capital One Tech
Published in
5 min readApr 24, 2020
starry solar system sky in shades of blue, orange, black, and yellow
Image credit: NASA/JPL-Caltech

In this blog post, I’d like to demonstrate how to create a demo JupyterLab extension that leverages React and Redux, along with TypeScript.

This is what we’ll end up with:

gif of a grey screen with white text and white buttons. as mouse glides over buttons white numbers count up/down

But how do we get there?

Documentation of React-enabled JupyterLab widgets is scarce and not always up to date. I hope to rectify that with this post where we’re going to do the following:

  1. Get started setting up our dev environment..
  2. Run cookiecutter to create the basic structure and dependencies of a new extension. NOTEYou don’t need to complete this step if you’re using my repository, this is already done for you.
  3. Set up React-specific parts of the app

Of course, if you’d rather learn by reading code, just go ahead and skip to my GitHub repository!

Getting Started

Note: Many of these steps are directly pulled from this tutorial on creating JupyterLab extensions. If you’re unfamiliar with JupyterLab Extensions, this tutorial is a great resource!

If you don’t use conda, you can achieve similar results with other virtual environment managers. See here for other installation methods.

We’re going to use conda to manage all of our Python dependencies.

We’re also going to create a new conda environment called jl-extension-env.

Run the following command in your terminal:

conda create -n jl-extension-env -c conda-forge jupyterlab cookiecutter nodejs git

We can activate and deactivate this environment like so (we’ll automate this using iTerm2 in a moment).

# Activate this environment when working on this repository
conda activate jl-extension-env
# Deactivate it otherwise
conda deactivate

Running cookiecutter

Note: skip this step if you’re just copying my repository as a starting point. This has already been done for you.

Run the following command wherever you’d like to create your new project.

cookiecutter https://github.com/jupyterlab/extension-cookiecutter-ts

Answer the prompts and then navigate into the newly created directory.

Installing the Extension Locally

Let’s install dependencies and link our development version of the extension with JupyterLab.

# Install dependencies and build the Typescript source
yarn && yarn build
# Link your development version of the extension with JupyterLab
jupyter labextension link .

You can watch the source directory for changes. Use the following commands.

yarn watch
jupyter lab — watch

Or, try my script to automatically open two tabs and run watch mode in both of them.

#!/bin/sh# Replace this with the path to your repository
REPOSITORY_DIR=/Users/davisford/Documents/sample-react-jupyterlab-extension
# Replace with the name of your conda environment
CONDA_ENV=jl-extension-env
# Helper function.
# Opens a new tab and executes a command in iTerm2
# Credit: https://apple.stackexchange.com/questions/110778/open-new-tab-in-iterm-and-execute-command-therenewtabi(){osascript \
-e ‘tell application “iTerm2” to tell current window to set newWindow to (create tab with default profile)’\
-e “tell application \”iTerm2\” to tell current session of newWindow to write text \”${@}\””
}
# Open a new tab and start running the local web dev server
newtabi “conda activate ${CONDA_ENV} && cd ${REPOSITORY_DIR} && yarn watch”
# Open another new tab and open up JupyterLab
newtabi “conda activate ${CONDA_ENV} && cd ${REPOSITORY_DIR} && jupyter lab — watch”

view raw
jupyterlab_dev.sh hosted with ❤ by GitHub

Working with React and Redux

Okay, so the gist of how a JupyterLab extension works is this…

There is an extension object that runs everything (located in index.ts). A MainAreaWidget is created, and we will attach our React app to that widget by extending JupyterLab’s ReactWidget.

We create a new class called ReactAppWidget, which extends ReactWidget. We override the render function and set up our <Provider> (which will handle our Redux store), and we render our <AppComponent>.

import React from ‘react’
import { ReactWidget } from ‘@jupyterlab/apputils’
import { Provider } from ‘react-redux’
import store from ‘../ducks/store’
export class ReactAppWidget extends ReactWidget {
constructor() {
super()
}
render(): JSX.Element {
return (
<Provider store={store}>
<AppComponent />
</Provider>
)
}
}
// Write all of your React here
const AppComponent = (): JSX.Element => {
return (
<div>
{/* Your app here */}
</div>
)
}

view raw
App.tsx hosted with ❤ by GitHub

Note: I haven’t experimented with Context yet in the JupyterLab setting, but theoretically you would wrap your ContextProvider around <AppComponent /> here as usual.

That’s pretty much it! <AppComponent /> can be any valid React that you’d like — this render function is similar to a normal React app’s index.tsx entry point.

I’ve used redux-toolkit to manage the application’s store — if you’re not familiar with redux-toolkit, it is essentially the CreateReactApp of Redux. It provides a set of boilerplate best practices to get your project hooked up and running quickly. I personally love this library.

Go forth and develop!

You’re all ready to go! Start both watch modes, and you’re in business!

Once JupyterLab opens in your browser, click the “Commands” icon, and search for “Sample React Redux Extension”.

screenshot of a white menu with black text sections bolded to match what was searched and the top row highlighted in grey

You’ll then see this neat app!

gif of a grey screen with white text and white buttons. as mouse glides over buttons white numbers count up/down

Congratulations! You’re ready to begin working on your very own React/Redux JupyterLab extension!

I hope this guide has been helpful. If you have any issues getting this to work, please open an issue on my GitHub repository.

Troubleshooting and Other Issues

There is some funky behavior related to closing and re-opening the extension in the same session.

  • It is probably 100% my fault. I’ll fix the repo if/when I figure it out.
  • For now, this is left as an exercise for the reader :)

Sometimes watch mode doesn’t seem to refresh very well. Try manually refreshing your page in the browser after making changes.

  • If that doesn’t work, just close your JupyterLab tabs and re-open them,

If you get build errors when uninstalling the extension locally, try unlinking it as well

  • Use this:
jupyter labextension unlink [your-extension-name]

Originally published at https://daviseford.com on April 24, 2020.

DISCLOSURE STATEMENT: © 2020 Capital One. Opinions are those of the individual author. Unless noted otherwise in this post, Capital One is not affiliated with, nor endorsed by, any of the companies mentioned. All trademarks and other intellectual property used or displayed are property of their respective owners.

--

--

Davis E Ford
Capital One Tech

Principal Software Engineer @ Capital One. AWS Certified Professional. React/Typescript