<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jupyter Blog - John Waidhofer</title><link href="https://jasongrout.github.io/medium-archive/pelican/" rel="alternate"/><link href="https://jasongrout.github.io/medium-archive/pelican/feeds/author-john-waidhofer.atom.xml" rel="self"/><id>https://jasongrout.github.io/medium-archive/pelican/</id><updated>2021-08-05T18:00:00+00:00</updated><subtitle>The Project Jupyter blog: news, releases, and community stories, archived from blog.jupyter.org.</subtitle><entry><title>Build a Jupyter Widget with React and TypeScript</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2021/build-a-jupyter-widget-with-react-and-typescript/" rel="alternate"/><published>2021-07-30T19:02:00+00:00</published><updated>2021-08-05T18:00:00+00:00</updated><author><name>John Waidhofer</name></author><id>tag:jasongrout.github.io,2021-07-30:/medium-archive/pelican/posts/2021/build-a-jupyter-widget-with-react-and-typescript/</id><summary type="html">&lt;p&gt;When using a Jupyter Notebook to work on data science projects, I’ve found small user interface abstractions to be very useful. Scrubbing a…&lt;/p&gt;
</summary><content type="html">&lt;figure&gt;
&lt;img alt="Photo by Adi Goldstein on Unsplash" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/build-a-jupyter-widget-with-react-and-typescript/images/001-0_lnZS93JgKwZAZD1m.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Photo by &lt;a href="https://unsplash.com/@adigold1?utm_source=medium&amp;amp;utm_medium=referral"&gt;Adi Goldstein&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When using a Jupyter Notebook to work on data science projects, I’ve found small user interface abstractions to be very useful. Scrubbing a slider or uploading a file can be more intuitive than editing a script and running it manually. This is especially true when a notebook is shared with others, since most people already have a basic knowledge about user interface elements.&lt;/p&gt;
&lt;p&gt;Jupyter Widgets are fantastic tools for simplifying Jupyter Notebook workflows with custom user interfaces. While widgets are versatile and composable, sometimes the default implementations provided by Jupyter don’t have the exact functionality we are looking for. Luckily, we are able to create Custom Widgets to suit our needs using web technologies. To get an idea of how Custom Widgets work, we are going to build a sleek color picker for JupyterLab using React.&lt;/p&gt;
&lt;h2 id="setup"&gt;Setup&lt;/h2&gt;
&lt;p&gt;Before we can start coding, there is some basic boilerplate to download. Start by installing &lt;code&gt;cookiecutter&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install cookiecutter
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then download the React widget boilerplate code.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cookiecutter https://github.com/Waidhoferj/jupyter-widget-react-cookiecutter.git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;cookiecutter&lt;/code&gt; template will guide us through some setup questions. The &lt;code&gt;author_name&lt;/code&gt;, &lt;code&gt;author_email&lt;/code&gt;, and &lt;code&gt;github_project_name&lt;/code&gt; fields are the most important. We can leave the others blank or go with the default values.&lt;/p&gt;
&lt;p&gt;Next, create a development environment using &lt;a href="https://conda.io/projects/conda/en/latest/user-guide/install/index.html"&gt;Anaconda&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda create -n jupyter-react-widget -c conda-forge nodejs yarn python jupyterlab
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda activate jupyter-react-widget
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Every package we install for this project will now be contained in a neat bundle.&lt;/p&gt;
&lt;p&gt;Inside the project directory created by &lt;code&gt;cookiecutter&lt;/code&gt;, run the following script to install the dependencies that we’ll use to build the widget. Then connect our widget environment to JupyterLab:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install -e &amp;quot;.[test, examples]&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;jupyter labextension develop --overwrite .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;yarn run build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Thats it for the setup! Let’s see what the default widget can do.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="interacting-with-the-widget"&gt;Interacting with the Widget&lt;/h2&gt;
&lt;p&gt;To test out the widget, run &lt;code&gt;jlpm watch&lt;/code&gt; in the project directory and open up JupyterLab with &lt;code&gt;jupyter lab&lt;/code&gt; in another terminal window. In JuypterLab, open the notebook file located at &lt;em&gt;&lt;project&gt;/examples/introduction.ipynb&lt;/em&gt;. Run all of the notebook cells to examine the state of the widget. In its current form, the widget displays a greeting alongside an input text field. Typing into the input field will update the target of the greeting. The value of the text box is automatically synced with &lt;code&gt;w.value&lt;/code&gt;, allowing us to access the input’s contents as a Python string. With the default setup working, we can start designing our own custom widget.&lt;/p&gt;
&lt;h2 id="building-a-color-picker"&gt;Building a Color Picker&lt;/h2&gt;
&lt;p&gt;Let’s take a look at the architecture of a Custom Widget. The Jupyter Widget system allows developers to send data between Python and TypeScript using a Model-View-Controller Architecture. The Python Widget Model represents the state of the widget. The TypeScript Controller edits and responds to changes in the Python model based on user input. These updates are rendered in the notebook using React.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Data is passed from the Python model to the TypeScript controller to the React view and back" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/build-a-jupyter-widget-with-react-and-typescript/images/002-1_cEy6OgSDz854q4Z0VJ59aQ.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Data is passed from the Python model to the TypeScript controller to the React view and back&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We can start building our color picker in &lt;em&gt;example.py&lt;/em&gt; by adding a color property to the model.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOMWidget&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;# Your widget state goes here. Make sure to update the corresponding&lt;/span&gt;
    &lt;span class="c1"&gt;# JavaScript widget state (defaultModelProperties) in widget.ts&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Jupyter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Jupyter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The color property is set equal to the &lt;code&gt;Unicode&lt;/code&gt; traitlet, which represents a string of characters. The &lt;code&gt;.tag(sync=True)&lt;/code&gt; method syncs the value of the color property with the TypeScript widget state.&lt;/p&gt;
&lt;p&gt;In &lt;em&gt;widget.ts&lt;/em&gt;, we define the corresponding TypeScript representation of color in the &lt;code&gt;defaultModelProperties&lt;/code&gt; object. This object mirrors the structure of the Python model, allowing us to access the model state in our frontend code.&lt;/p&gt;
&lt;figure&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;defaultModelProperties&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figcaption&gt;
&lt;p&gt;Add color to the default model properties object&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Now that we have defined our model, the rest of the work can be done in React. If you’d like to flex your web development skills and design a custom color picker, go for it! Alternatively, we can get a great plug-and-play interface by installing &lt;code&gt;react-colorful&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;jlpm add react-colorful
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Create a &lt;em&gt;ColorPicker.tsx&lt;/em&gt; file in the &lt;em&gt;src&lt;/em&gt; folder. We can build out the component in a few lines of code:&lt;/p&gt;
&lt;figure&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HexColorPicker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;react-colorful&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useModelState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./hooks/widget-model&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ColorPicker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setColor&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useModelState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;HexColorPicker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setColor&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figcaption&gt;
&lt;p&gt;The ColorPicker React component&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Let’s walk through what’s happening here. At the top of the file, we import React, the react-colorful component, and a hook called &lt;code&gt;useModelState&lt;/code&gt;. The &lt;code&gt;useModelState&lt;/code&gt; hook operates like the &lt;a href="https://reactjs.org/docs/hooks-state.html"&gt;&lt;code&gt;useState&lt;/code&gt; hook&lt;/a&gt; but instead of accepting a default value as a parameter, it takes the name of a property from our Python model. The hook will automatically update the React view when the referenced property updates in Python. Also, the React widget can directly update the Python model with the &lt;code&gt;setColor&lt;/code&gt; function. By passing &lt;code&gt;color&lt;/code&gt; and &lt;code&gt;setColor&lt;/code&gt; to the &lt;code&gt;HexColorPicker&lt;/code&gt;, the Python widget model will reflect the chosen color whenever the user makes a selection in the interface.&lt;/p&gt;
&lt;p&gt;To display the color picker in our notebook, add the component to &lt;em&gt;ReactWidget.tsx&lt;/em&gt;, which is the top level React file in the widget system. Import the &lt;code&gt;ColorPicker&lt;/code&gt; component at the top of the file and make it the return value of &lt;code&gt;ReactWidget&lt;/code&gt;. We can delete all of the boilerplate within the &lt;code&gt;ReactWidget&lt;/code&gt; component in the process. Additionally, delete the &lt;code&gt;useModelState&lt;/code&gt; import at the top of the file, since it is no longer needed.&lt;/p&gt;
&lt;figure&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ColorPicker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./ColorPicker&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ReactWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;WidgetProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ColorPicker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figcaption&gt;
&lt;p&gt;Add ColorPicker to ReactWidget&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To test this out, &lt;a href="https://support.labs.cognitiveclass.ai/knowledgebase/articles/857388-how-to-restart-the-jupyter-kernel"&gt;restart the python kernel&lt;/a&gt; in JupyterLab and refresh the browser window. We will now see a color picker where the input field used to be. Pick a color and print out &lt;code&gt;w.color&lt;/code&gt; to get the hex code in Python. Congrats! You now know how to create Jupyter Widgets using React!&lt;/p&gt;
&lt;p&gt;The principles that we covered here can be applied to create more complex applications. To add additional properties, define them in both &lt;em&gt;example.py&lt;/em&gt; and &lt;em&gt;widget.ts&lt;/em&gt;, then interact with the data via the &lt;code&gt;useModelState&lt;/code&gt; hook in a React component. Go build some amazing widgets!&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Waidhoferj/jupyter-widget-react-cookiecutter#hooks"&gt;Additional React Jupyter Widget Hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Custom.html#Other-traitlet-types"&gt;Data types for the Python Model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Custom.html"&gt;The official IPythonWidget tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="JavaScript"/><category term="widgets"/></entry></feed>