How to Render Jupyter Notebooks in Your Web (React) App
Jupyter notebooks are great for experimentation, teaching and learning. One way to make them even more powerful/impactful (especially for teaching) is to make your notebook available as a web page that can be viewed without the need for a specific python (or other language) environment (e.g., Colab, Kaggle, or Binder).
I have written a few pedagogical notebooks and I wanted to explore ways to organize/integrate them into a web application (Gatsby) where they can be browsed easily and then launched if needed. My key requirements for this project included:
- Support a workflow where I can update/maintain the notebook directly and those changes are automatically synced with my web application;
- Style the notebook to fit the web application design language and support custom behaviours (e.g., click events).
This post discusses my process in addressing these requirements and covers the following:
- Pros and cons of two high level approaches to rendering Jupyter Notebooks in your web application
- The approach I used: Create a script to convert notebooks to
gatsbyweb app using
iframes. As you will see, this is not the only approach, but it addresses my requirements above.
Note: The idea of Jupyter notebooks on the web is not new. If you are not particular about controlling the details of the notebook, the
fastpages3 package from the incredible folks at
fast.ai lets you create an entire blog completely from Jupyter notebooks. Also see Mkdocs1, a library for generating documentation sites from markdown files and jupyter notebooks2. Or Quarto4.
If you have opened a Jupyter notebook file (
.ipynb file extension) in a text editor, what you see is a json document. This json content (which we will refer to as
jupyter json) is rendered in cells and made interactive in the jupyter notebook interface. The example below is the
json for a jupyter notebook with a single code cell:
Now that we know what's in an
.ipynb file, the question is - how do we go from json to beautiful cells of text, formatted code and images that display nicely in the browser? There are two high level approaches - i.) directly parsing/rendering
jupyter json and ii.) using
nbconvert to convert
jupyter json to other formats such as
In theory, you can load a notebook file, parse the content as
json and write your own React component to render its content (see pseudocode snippet below).
While this approach offers the most control, it is also the most effortful as you will need to fully understand the Jupyter json structure and also implement layout/styling (e.g., how to style code cells, output cells, parse & render markdown cells, code syntax highlighting, table formatting where applicable etc.). It is a lot of work! And perhaps unnecessary work.
See this open source project which attempts to implement a React component that loads a Jupyter notebook via url, parses and renders its content.
It turns out that you can leverage layout/design decisions made by others who have spent time designing and developing Jupyter Notebooks. Hello nbconvert!nbconvert is a Python package that converts Jupyter Notebooks to multiple target formats includingHTML, Markdown, LaTeX and ReStructuredText . Nbconvert provides default templates that govern the visual layout and style for each of these target formats. Results from nbconvert (which are already styled) are easier to include in your web application.
I initially explored the markdown format as my Gatsby application already had a blog section based on remarkdown (a format that that allows writing JSX inside markdown files). However, I found that the markdown format generated by nbconvert were not immediately compatible with Gatsby causing parse errors. In addition, it can be challenging to visually style markdown and add interactive click behaviors. In theory, you can write a custom template to govern the generation of markdown files using the nbconvert templating system; however, again this route felt like unnecessary work.
I ended up using the following
nbconvertto convert the notebook to
- Wrote some custom
cssstyling to format cells and output; add custom
- Wrote a script in
gastsby-node.jsto automatically convert all notebooks in a directory to
htmland copy to the Gatsby static folder
- Wrote a React component to load these html files and render them in iframes.
Given that I have some experience with css and html, I found it easier to focus my effort on writing css rules to style/control the notebook.
Under the hood, nbconvert uses Jinja templates (
.j2 file extensions) to determine how to render a notebook. If you need some custom preprocessing or a specific output format for your notebook, creating a new template file (which can inherit from any of the supported base templates) is the way to go.
To create a custom template, you will need to do the following:
- Find the
templatesdirectory for nbconvert. You can run
jupyter --pathsto see directories that jupyter will scan to discover your templates. Learn more here. Note that you might find your templates directory in a different location depending on your system. In this case, you can add a symbolic link that maps your templates directory to a path jupyter is scanning.
- Create a new folder in the nbconvert
templatesdirectory for your template. In my case, I copied the
labtemplate, renamed it to
dml/static/theme-xx.cssas needed. The goal is to identify the css classes linked to the layout elements you want to modify and update them in the
cssfiles. See the
nbconverttemplate documentation here for more details.
Some of the css changes I made were to hide numbering in cells, add custom borders to code cells (
.jp-InputArea-editor class), increase font-size, add copy code button to code cell (see section below) etc.
Once you are done styling, you can then run nbconvert using your custom template.
Once your notebooks are converted to html, you can create at React component that takes a
url and renders an iframe with the html.
Note: There may be some limitations to the content of interactive notebook cells e.g., some python widgets such as visualization widgets may not render properly.
To do this, you can add a button to the
base.html.j2 jinja template in the
dml directory. The snippet below adds a button all code cells.
I added the following snippet to my
gastsby-node.js file. Note that code in
gastsby-node.js is executed once in the process of building your site. Alternatively, you can write a similar script in your language of choice that achieves the same goal.
The snippet above scans the
notebooks folder and converts all notebooks to html in the
Based on my experience building out the approach above, here are some general recommendations for rendering notebooks on the web.
If you want low level customization (e.g.. complete control over content, formatting, transformation, etc) - you can load, parse and render your ipynb files directly. Note this is route is the most effortful.
If you would a relatively fast method to render notebooks on the web that integrates well into your existing application and stack , use nbconvert to generate styled html (or other format) and customize as discussed in this post.
If you just want notebooks on the web without any customization .. use existing tools like fast pages.
This post discussed several approaches to parsing and rendering Jupyter notebooks. I hope that this post has helped you to understand the Jupyter Notebook format, how to convert it other formats using nbconvert and how to style and integrate it into your web application.
Have you worked on something similar? Or trying out some other approach? Reach out on twitter!
And yes, I look forward to sharing the "organized" collection of notebooks once I have made some more progress.
- Fastpages: An easy to use blogging platform with extra features for Jupyter Notebooks. https://fastpages.fast.ai/↩
- MkDocs: MkDocs is a fast, simple and downright gorgeous static site generator that's geared towards building project documentation https://www.mkdocs.org/↩
- mkdocs-jupyter: Use Jupyter Notebooks in mkdocs https://github.com/danielfrg/mkdocs-jupyter↩
- Quartois an open-source scientific and technical publishing system built on Pandoc https://quarto.org/↩