yt coffee

Study hard, play harder.

Display Jupyter Notebook on Next.js

  • 2019-01-15 11:17
  • #Blog dev

Commit: yuku/yuku.github.io@1c18e26

Jupyter Notebook can now be displayed on this blog. I'm going to use it to publish my study records of machine learning and data analysis. 💪🏼

print("Hello world")
Hello world

This article describes the details of the implementation.

Display ipynb files on Next.js

In order to display ipynb files on Next.js, I needed to do two things:

  1. Make a ipynb file readable by Webpack.
  2. Convert a loaded ipynb file into a React Component.

Make ipynb files readable

Next.js uses Webpack for file resolution, so you can use ipynb-loader. This loader does nothing more than call jupyter/nbconvert internally and return the result. jupyter/nbconvter provides the ability to convert ipynb files to HTML, Markdown, etc.

When using jupyter/nbconvert, I had to decide whether to output as HTML or Markdown

If outputting in HTML, I can easily display the characteristic In [1] prompt of Jupyter Notebook, but on the other hand, it is difficult to use some desired features such as LaTex notation. 1

When outputting as Markdown, not only I can use the same processing as MDX, but I can also use Markdown notation that Jupyter Notebook does not support, which is an advantage. On the other hand, the correspondence between input and output is lost, so it doesn't feel like a Jupyter Notebook is displayed.

After all, when I want to write a blog in ipynb format, I want to express the correspondence between input and output, and if I want to use LaTex, for example, I can write in MDX format, so I decided to output in HTML.

Convert to a React Component

Next.js expects to import a React Component. This means that Webpack needs to generate a string that can be interpreted as a React Component.

JSX is useful for writing a React Component. Unfortunately, I couldn't find a Webpack loader that would accomplish this goal, so I implemented such loader by myself using htmltojsx, which is written by Facebook:

const HTMLtoJSX = require("htmltojsx")
const converter = new HTMLtoJSX({ createClass: false })

module.exports = function (content) {
  // The `content` argument is the HTML returned by ipynb-loader.
  return `
  import React from "react"
  export default () => ${converter.convert(content)}
  `
}

Notes

  • I've been generated entries.ts file, which is a collection of metadata written in MDX files during compilation, but ipynb doesn't support such metadata, so I gave up on automatically generating the file and decided to manage it manually.
  • Using jupyter/nbconvert to generate HTML from ipynb made the Blog's architecture complex, and I had to switch from Travis CI to CircleCI to maintain auto-deployment. Since ipynb is a JSON, it may be good to reimplement it with JavaScript. In that case, it seems that the part that converts Markdown to HTML can be shared with MDX.

Conclusion

In my next job, I'm going to use pyspark to analyze data, so I'd like to use this feature to publish the knowledge I've gained while leaning pyspark and using it.

This is the first time I've written a webpack loader, and it's easier than I thought it would be. Through this implementation, I have learned Webpack itself well. If you feel that you don't know what Webpack is doing, you may be able to understand Webpack better by writing a loader.


  1. Jupyter Notebook implements LaTex notation using MathJax, but in this blog, I try not to run JS on the browser as much as possible, so I use remark-math and rehype-katex to convert LaTex notation to HTML at compile time.