How to Write a Jupyter Notebook Extension | by Will Koehrsen | Towards Data Science

Jupyter Notebook Extensions are simple add-ons which can significantly improve your productivity in the notebook environment. They automate tedious tasks such as formatting code or add features like creating a table of contents. While there are numerous existing extensions, we can also write our own extension to do extend the functionality of Jupyter.
In this article, we’ll see how to write a Jupyter Notebook extension that adds a default cell to the top of each new notebook which is useful when there are libraries you find yourself importing into every notebook. If you want the background on extensions, then check out this article. The complete code for this extension is available on GitHub.
The final outcome of the Default cell extension is shown below:
(If you don’t yet have Jupyter Extensions, check out this article or just run the following code in a command prompt: pip install jupyter_contrib_nbextensions && jupyter contrib nbextensions install and then start a new notebook server and navigate to the extensions tab).
Unfortunately, there’s not much official documentation on writing your own extension. My tactic was to read the other extensions, copy and paste copiously, and experiment until I figured it out. This Stack Overflow question and answer provided the basic code for this extension.
There are 3 parts (at minimum) to any Jupyter Notebook extension:
description.yaml : A configuration file read by Jupytermain.js : The Javascript code for the extension itselfREADME.md : A markdown description of extension
description.yaml : A configuration file read by Jupyter
main.js : The Javascript code for the extension itself
README.md : A markdown description of extension
(We can also have more functions in other files or css for styling).
These 3 files should live in a single directory which we’ll call default_cell . This folder, in turn, needs to be in the nbextensions subdirectory of the jupyter_contrib_extensions library (what you installed with pip. To find the location of a library installed with pip run pip view package. )
My jupyter_contrib_extensions directory is:
/usr/local/lib/python3.6/site-packages/jupyter_contrib_nbextensions/nbextensions
So the file structure looks like (with nbextensions as shown above):
nbextensions/        default_cell/        - description.yaml        - main.js        - README.md
When you run jupyter notebook, Jupyter looks in this location for extensions, and shows them on the extensions tab on the server:
When you make a change to the extension files while developing and you want to see the effects in a Jupyter Notebook,you need to run the command jupyter contrib nbextensions install to rewrite the Jupyter config files. Then, restart the notebook server to see your changes.
With the details out of the way, let’s go through the three files we need.
YAML (YAML Ain’t Markup Language) is a human-readable standard for writing configuration and header files. The YAML file describes the extension to the Jupyter Extensions Configurator to be rendered on the extensions tab.
This is a fairly easy file to copy + paste and modify to our needs.
This is then rendered nicely on the NBExtensions tab in the notebook:
The compatibility indicates the versions of Jupyter for which the extension works. I just added all of them (3.x — 6.x) and it seemed to work fine!
This is the heart of the application where the actual logic for the extension lives. Jupyter Notebooks are run in the browser, which means that extensions must be written in Javascript, the language of the web.
It can be a little difficult to figure out what commands to use to make the notebook do what you want. One way to experiment is using the Chrome developer tools (cntrl + shift + i on Windows) or right-click > inspect.
With the developer tools open, we can use the console tab to run commands.
Try opening the developer tools in a Jupyter Notebook and play with options that start with Jupyter.notebook. Any commands you enter will have to be in Javascript. An example of this behavior can be seen in the clip below. I open up the developer tools and then run a few commands to execute cells, insert a new cell, and select the previous cell.
Developing the Javascript code required a lot of experimenting like this! It also helps to read the other extensions to figure out what you need to do.
The final main.js is below:
The most important part of the code is the add_cell function.
var add_cell = function() {    Jupyter.notebook.    insert_cell_above('code').    // Define default cell here    set_text(`Define default cell here`);    Jupyter.notebook.select_prev();    Jupyter.notebook.execute_cell_and_select_below();};
This adds a code cell above the currently selected cell with the python code written in the parenthesis (in the set_text call). This is where the default code cell should be defined. The function then executes the cell and selects the one below.
The load_ipython_extension function first checks the number of cells in the notebook. If there is only one cell — a new notebook — it calls the add_cell function which places the default cell at the top of the notebook.
// Run on startfunction load_ipython_extension() {// Add a default cell if a new notebook    if (Jupyter.notebook.get_cells().length===1){        add_cell();    }    defaultCellButton();}
It then runs the defaultCellButton function which places a button on the Jupyter Notebook toolbar. We can use this button to add and run the default cell above the currently selected cell. This could be useful when we have an already started notebook and we want our normal imports.
There is an almost unlimited number of tasks we could accomplish with Javascript in the browser. This is only a simple application, but there are many more complex Jupyter extensions (such as the Variable Inspector) that demonstrate more of the capabilities. We can also write applications in Javascript that call on Python scripts for even greater control.
A readme markdown file should be familiar to anyone with even a little experience programming. Here is where we explain what our application does and how to use it. This is displayed on the extensions tab:
(The actual code is pretty boring but for completeness, here it is)
Default Cell=========Adds and runs a default cell at the top of each new notebook. To change the default cell, edit the `add_cell` function in the `main.js` file. The relevant section is`Jupyter.notebook.insert_cell_above('code', 0).set_text(``)`Set the `text` to whatever you would like.This extension also adds a button to the toolbar allowing you to insert and run the default cell above your current cell. This can be helpful if you open an already started notebook and notice you are missing some common imports.This extension is a work in progress and any help would be appreciated. Feel free to make contributions on GitHub or contact the author (Will Koehrsen) at wjk68@case.edu
Once you have the three required files, your extension is complete.
To see the extension working, make sure the default_cell directory is in the correct location, run jupyter contrib nbextensions install and start up a Jupyter Notebook server. If you navigate to the NBExtensions tab, you will be able to enable the extension (if you don’t have the tab, open a notebook and got to Edit > nbextensions config).
Start up a new notebook and see your extension at work:
This extension isn’t life-changing, but it might save you a few seconds!
Part of the joy of gaining computer literacy is realizing that if you want to accomplish something on a computer, chances are that you probably can with the right tools and willingness to learn. This small example of making a Jupyter Notebook extension demonstrates that we are not limited by what we get out of the box. We just need a few lines of code to accomplish our goals.
Hopefully, either this extension proves useful to you or inspires you to write your own. I have gotten a lot of use from extensions and am looking forward to seeing what else people can develop. Once you do develop an extension, share it so others can marvel at your code and benefit from your hard work.
As always, I welcome feedback and constructive criticism. I can be reached on Twitter @koehrsen_will or through my personal website willk.online.