Hi everyone! Hope you are all doing well! In this newsletter issue, I'll discuss some of my recent experiments with attempting to render beautiful, styled (custom themed) Jupyter notebooks in web pages.
For context, I have written a few pedagogical notebooks and I wanted to explore ways to organize/integrate them into a web application where they can be browsed easily and then launched in an environment like Colab or Kaggle if needed. My 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;
Support custom styling of the notebook to fit the web application design language and support custom behaviours (e.g., custom buttons, click events).
This newsletter summarizes my process in addressing these requirements.
Jupyter Notebooks on the Web? Why?
IMO, the primary benefit of investing in a pipeline that allows you render your notebooks in a web app comes from the ease of having a single environment where you can simultaneously code and teach/explain/blog. If you have attempted to write technical tutorials, the hassle of adding code snippets to your blog posts, and then manually updating those snippets as the underlying scripts change/evolve can be a hassle.
A better workflow is to simply focus on creating a high quality notebook that contains both explanations and runnable code, and have any changes automatically integrated into your web application.
This concept is not entirely new - in fact, there are a few packages that allow you do something similar. For example, the Fastpages package lets you create an entire blog completely from Jupyter notebooks. Also see Mkdocs, a library for generating documentation sites from markdown files and jupyter notebooks. While these tools work well, it can be challenging to style them, or integrate them with an existing web application build process (they frequently are tied to other frameworks/languages that may not play well with your specific setup).
In my case, I focused on creating my own workflow mostly to exercise as much control over the styling, layout, create custom interaction behaviors for the resulting web pages, and above all integrate it into the automatic build process for my web application.
How to Render Notebooks on the Web?
Underneath, Jupyter notebooks are json files that holds content for code cells, markdown cells and corresponding output. Given this, it is possible to load a notebook file as json and attempt to generate html elements based on its content.
While this approach offers quite a bit of control, it is also 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.
The approach I used primarily relies on nbconvert. nbconvert is a Python package that converts Jupyter Notebooks to multiple target formats including HTML, Markdown, LaTeX, ReStructuredText and other formats. It provides default templates that govern the visual layout and style for each of these target formats. Results from nbconvert (which are already styled) are consequently easier to include in a 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 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 html
approach:
Use
nbconvert
to convert the notebook tohtml
Wrote some custom
css
styling to format cells and output; add customhtml
andjs
behaviors.Wrote a script in
gastsby-node.js
to automatically convert all notebooks in a directory tohtml
and copy to the Gatsby static folderWrote 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 as opposed to rendering other formats like markdown, latex etc.
Results ...
As an example, below are pictures of what a sample notebook looks like in Colab and then when rendered in my web application.
Learn More ..
There are a few open questions related to rendering jupyter notebooks in web pages that were not covered in this newsletter issue. For example:
What is the Jupyter notebook file format like (`.ipynb` files) and can they be rendered directly in a web application? Hint: yes, but with some caveats!
How can we add custom styles to output html provided by nbconvert? Any caveats with using other formats like markdown?
How can we add custom html elements and javascript click behaviours to the generated html?
What are example code snippets for the tasks listed above?
I have written a blog post that explores these areas in more detail, with code snippets.