How to blog with Jupyter Notebook

Elan Ding
Modified: June 9, 2018

Github Repository


Jupyter Notebook makes sharing ideas incredibly easy. All one need is the .ipynb file and type in the command line

jupyter notebook

to view the file in the browser.

However, at some point, I want to be able to present my work to non-technical people who have never heard of Python or Jupyter Notebook. This is where the wonderful nbconvert comes in. With Jupyter and Python installed, I can simply enter the following into the command line

jupyter nbconvert --to html myfile.ipynb

The output will be myfile.html, which can be opened by any modern web browser.

However, the .html file obtained from nbconvert has a fixed style, so it does not look so good if I want to incorporate that into my blog. In particular, it is missing a navigation bar, so I cannot go back to my homepage! I did some research, and found this excellent post. I made some simplifications and customizations. Hope this can help those who are interested in blogging with Jupyter.


The first step is to create an empty HTML template so that we can manually inject all the stylings later.


<!DOCTYPE html>

Notice that the blocks math_block, css_block, navbar_block, body_block, and js_block are for customization purposes. Feel free to name them whatever you want.

Next, we are going to create some .txt files that correspond to these customization blocks. (I provide my examples here, which will not work for you. You need to modify that to match your own website's styles.)


<nav class="navbar navbar-default">
    <div class="container text-center">
        <div id="navbar" class="navbar-collapse">
            <ul class="nav navbar-nav">
                <li><a href="../../index.html">Home</a></li>
                <li><a href="../../projects.html">Projects</a></li>
                <li><a href="../../blog/index.html">Blog</a></li>
                <li><a href="../../files/cv.pdf">CV</a></li>


<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Elan Ding</title>
<link rel="stylesheet" href="../../assets/css/bootstrap.min.css" />
<link rel="stylesheet" href="../../assets/css/elan-custom.css" />

Note that the file bootstrap.min.css is the bootstrap custom theme obtained here. My own custom css file is stored in elan-custom.css. My custom customization, which includes something like this:

body {
    font-family:  "Arial Narrow Bold", sans-serif;
    font-size: 21px;
    line-height: 1.42857143;
    color: #777777;
    background-color: #ffffff


<script src="../../assets/js/jquery.min.js"></script>
<script src="../../assets/js/jquery.scrolly.min.js"></script>
<script src="../../assets/js/skel.min.js"></script>
<script src="../../assets/js/skel-viewport.min.js"></script>
<script src="../../assets/js/util.js"></script>
<script src="../../assets/js/main.js"></script>


<script src='' async></script>

<script type="text/x-mathjax-config">
    extensions: ["tex2jax.js"],
    jax: ["input/TeX", "output/HTML-CSS"],
    tex2jax: {
      inlineMath: [ ['$','$'], ["\\(","\\)"] ],
      displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
      processEscapes: true

Notice that I did not create the file for body_block yet. This will be the main body of the html file, which comes from using nbconvert. Let's do that using Python.

Converting to html

If you don't have nbconvert installed, simply type

pip install nbconvert

in your terminal to install it. Then, create this Python script:


#!/usr/bin/env python

import os
from subprocess import Popen
import sys

inFile = sys.argv[1]
Popen('jupyter nbconvert '+inFile +' --template basic', shell=True).wait()
newFile = os.path.splitext(inFile)[0]+'.html'

template = open('custom/template.html', 'r').read()

read_navbar = open('custom/navbar.txt', 'r').read()
read_css = open('custom/main_css.txt', 'r').read()
read_gs = open('custom/main_js.txt','r').read()
read_body = open(newFile, 'r').read()
read_mathjax = open('custom/mathjax.txt', 'r').read()

template = template.replace("navbar_block", "\n" + read_navbar + "\n")
template = template.replace("js_block", "\n" + read_gs + "\n")
template = template.replace("css_block", "\n" + read_css + "\n")
template = template.replace("body_block", "\n" + read_body + "\n")
template = template.replace("math_block", "\n" + read_mathjax + "\n")

with open(newFile, 'w') as f:

Basically this script converts your .ipynb file into a raw .html first. Then, it reads this file as the body_block in the template. Finally, it fills in all the missing pieces in template.html.

Final step

Finally, make sure all files are in the same directory, which looks something like this:

  • Jupyter-blog
    • custom
      • main_css.txt
      • main_js.txt
      • mathjax.txt
      • navbar.txt
      • template.html
    • yourfile.ipynb

Next, cd to that directory in your command line, and type

python yourfile.ipynb

There you go! You should see a new file yourfile.html, which integrates into your website. The files used here are available to download on Github.