Create a blog using Nikola static website generator
Tags: blogging, tutorial, python, nikola
Published: 2020-05-05 16:36:03

_You can also read that article on my [blog](https://www.brainsorting.dev/posts/create-a-blog-with-nikola/) or on [medium](https://medium.com/@mattioo/create-a-blog-using-nikola-static-website-generator-ecb30f73d9fb) !_

If you don't know what is Nikola, it is a static website/blog generator (like gatsby and other tools). It's written in Python and it is working out of the box for rendering markdown, rst, latex math formula and jupyter notebook files.

I like to understand what I am using, and pushing it to some limits to really get what I want from it, and making a blog with Nikola was no exception. Here I tried to summarize all the informations I found, and all the experimentation I did. I hope you'll enjoy ! 🙏🏼

## 1) Installation

The first step is to have Python 3 installed on your computer, I recommend using virtual environment management.

Once you have create your virtual environment :

```bash
pip install --upgrade pip setuptools wheel
pip install --upgrade "Nikola[extras]"
```
<!-- TEASER_END -->
## 2) Create the blog

After installing Nikola, creating your site will be very easy, just use the command `nikola init <directory_name>`. You can add the `--demo` argument if you want a website built with demo content.

All the configurations are done in a single `conf.py` file, at the root of your blog folder.

You can now build your site and see how it looks like. Use the command `nikola auto` to use a server with automatic rebuilds when changes is detected in your files. Visit [http://128.0.0.1:8000](http://128.0.0.1:8000) to see your site.

## 3) Add a Post

Now if you want to add a post in your blog you should use the command `nikola new_post`(the default use reStructuredText format, add `-f markdown` if like me you prefer to write in markdown). The CLI will ask for the title of your blog post and then create the file in the folder `posts/*.md`.

## 4) Enable Jupyter Notebook file format

Just add `*.ipynb` as recognizable formats:

```python
POSTS = (
	("posts/*.rst", "blog", "post.tmpl"),
	("posts/*.md", "blog", "post.tmpl"),
	("posts/*.txt", "blog", "post.tmpl"),
	("posts/*.html", "blog", "post.tmpl"),
	("posts/*.ipynb", "blog", "post.tmpl"), # new line
)
PAGES = (
	("pages/*.rst", "", "page.tmpl"),
	("pages/*.md", "", "page.tmpl"),
	("pages/*.txt", "", "page.tmpl"),
	("pages/*.html", "", "page.tmpl"),
	("pages/*.ipynb", "", "page.tmpl"), # new line
)
```

You can create a blog post with `nikola new_post -f ipynb` or add your jupyter notebook in your `posts` folder. Don't forget to add and configure these line in the metadata of your jupyter notebook file if you don't let nikola create the file for yourself :

```python
"nikola": {
   "category": "",
   "date": "2020-03-28 16:27:51 UTC+01:00",
   "description": "",
   "link": "",
   "slug": "jupyter-notebook-test",
   "tags": "",
   "title": "Jupyter Notebook Test",
   "type": "text"
  }
```

## 5) Using Markdown for your post

Nikola handle markdown files by default. The meta are auto generated when you use `nikola new_post` but I prefer to do it differently. Add the `markdown.extensions.meta` to your `conf.py` file.

```python
MARKDOWN_EXTENSIONS =
 ['markdown.extensions.fenced_code',
  'markdown.extensions.codehilite',
  'markdown.extensions.extra',
  'markdown.extensions.meta']
```

Now you can simply add these line on top of your markdown files, in a pelican style, to indicate Nikola all the information it needs to build your post :

```markdown
Title: Test post in markdown
Date: 2020-04-01
Slug: test-post
Tags: markdown, test
Categories: Tutorial
```

In my situation I decided to use `pandoc` instead of the default markdown compiler. I did this because I often have code blocks nested in numbered or bullet list and the default markdown compiler does not render those properly. It also looses the numbered list sometimes whereas `pandoc` is doing an absolute great job. Thanks to this [great blog post](https://mrinalpurohit.in/blog/moving-from-markdown-to-pandoc-in-nikola/) that was explaining how to do it !

To use `pandoc` instead of the default markdown you need to first install it. You can use `brew install pandoc` if you have a mac, or look [here](https://pandoc.org/installing.html) for more instructions. Then you can change those lines in the `conf.py` :

```bash
COMPILERS = {
    "rest": ('.rst', '.txt'),
    # "markdown": ('.md', '.mdown', '.markdown'),
    "textile": ('.textile',),
    "txt2tags": ('.t2t',),
    "bbcode": ('.bb',),
    "wiki": ('.wiki',),
    "ipynb": ('.ipynb',),
    "html": ('.html', '.htm'),
    # PHP files are rendered the usual way (i.e. with the full templates).
    # The resulting files have .php extensions, making it possible to run
    # them without reconfiguring your server to recognize them.
    "php": ('.php',),
    # Pandoc detects the input from the source filename
    # but is disabled by default as it would conflict
    # with many of the others.
    "pandoc": ('.md', 'txt'),
}
...
PANDOC_OPTIONS = ['-f', 'gfm', '--toc', '-s']
```

See how I commented the markdown compiler and uncommented `pandoc` for `.md` files. The last two `PANDOC_OPTIONS` (--toc and -s) are used for automatically generating a Table of Content on the HTML generated output.

Once you use pandoc for compiling your markdown file, for creating a new blog post in markdown you need yo use this command `nikola new _post -f pandoc` and not markdown anymore.

Adding solely the pandoc as markdown compiler is unfortunately not enough because we lose the ability to use the `CODE_COLOR_SCHEME = monokai` option in conf.py. One solution is to use `pandoc` generated css for one of its code highlight theme (kate in my case).

Create a `custom.css` file in `files/assets/css/` as [explained here](#tweaking-the-css) and add this css code :

```css
code {white-space: pre-wrap;}
span.smallcaps {font-variant: small-caps;}
span.underline {text-decoration: underline;}
div.column {display: inline-block; vertical-align: top; width: 50%;}

a.sourceLine { display: inline-block; line-height: 1.25; }
a.sourceLine { pointer-events: none; color: inherit; text-decoration: inherit; }
a.sourceLine:empty { height: 1.2em; position: absolute; }
.sourceCode { overflow: visible; }
code.sourceCode { white-space: pre; position: relative; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
code.sourceCode { white-space: pre-wrap; }
a.sourceLine { text-indent: -1em; padding-left: 1em; }
}
pre.numberSource a.sourceLine
  { position: relative; }
pre.numberSource a.sourceLine:empty
  { position: absolute; }
pre.numberSource a.sourceLine::before
  { content: attr(data-line-number);
    position: absolute; left: -5em; text-align: right; vertical-align: baseline;
    border: none; pointer-events: all;
    -webkit-touch-callout: none; -webkit-user-select: none;
    -khtml-user-select: none; -moz-user-select: none;
    -ms-user-select: none; user-select: none;
    padding: 0 4px; width: 4em;
    background-color: #ffffff;
    color: #a0a0a0;
  }
pre.numberSource { margin-left: 3em; border-left: 1px solid #a0a0a0;  padding-left: 4px; }
div.sourceCode
  { color: #1f1c1b; background-color: #ffffff; }
@media screen {
a.sourceLine::before { text-decoration: underline; }
}
code span. { color: #1f1c1b; } /* Normal */
code span.al { color: #bf0303; background-color: #f7e6e6; font-weight: bold; } /* Alert */
code span.an { color: #ca60ca; } /* Annotation */
code span.at { color: #0057ae; } /* Attribute */
code span.bn { color: #b08000; } /* BaseN */
code span.bu { color: #644a9b; font-weight: bold; } /* BuiltIn */
code span.cf { color: #1f1c1b; font-weight: bold; } /* ControlFlow */
code span.ch { color: #924c9d; } /* Char */
code span.cn { color: #aa5500; } /* Constant */
code span.co { color: #898887; } /* Comment */
code span.cv { color: #0095ff; } /* CommentVar */
code span.do { color: #607880; } /* Documentation */
code span.dt { color: #0057ae; } /* DataType */
code span.dv { color: #b08000; } /* DecVal */
code span.er { color: #bf0303; text-decoration: underline; } /* Error */
code span.ex { color: #0095ff; font-weight: bold; } /* Extension */
code span.fl { color: #b08000; } /* Float */
code span.fu { color: #644a9b; } /* Function */
code span.im { color: #ff5500; } /* Import */
code span.in { color: #b08000; } /* Information */
code span.kw { color: #1f1c1b; font-weight: bold; } /* Keyword */
code span.op { color: #1f1c1b; } /* Operator */
code span.ot { color: #006e28; } /* Other */
code span.pp { color: #006e28; } /* Preprocessor */
code span.re { color: #0057ae; background-color: #e0e9f8; } /* RegionMarker */
code span.sc { color: #3daee9; } /* SpecialChar */
code span.ss { color: #ff5500; } /* SpecialString */
code span.st { color: #bf0303; } /* String */
code span.va { color: #0057ae; } /* Variable */
code span.vs { color: #bf0303; } /* VerbatimString */
code span.wa { color: #bf0303; } /* Warning */
```

Now your code blocks should be highlighted. It's up to you to custom it further to change the color background or stuff like this.

I also wanted to style the table of content :

```css
.p-summary.entry-summary #TOC {
  display: None; /* disable showing the TOC in my blog home page teasers */
}

#TOC {
  background-color: #e9f8f8;
  border-radius: 3px;
  padding: 18px 0px 1px 6px;
  margin-bottom: 20px;
}
```

## 6) Pages vs Posts

Nikola has two type for entries on your website, POSTS and PAGES.

### POSTS

These are your blog posts. POSTS are added to feeds, indexes, tag lists and archives.

### PAGES

These are generally static pages that may be built when you design your website. Once your design will be done you should not been making many new pages.

For example in PAGES, I have the following pages:

- Resume (html)
- Cheatsheet (html)

## 7) Customizing the navigation bar

Customization of the navigation top bar is done, again, in the `conf.py` file.

```python
NAVIGATION_LINKS = {
    DEFAULT_LANG: (
        ("/resume/", "Resume"),
        ("/cheatsheet/", "Cheatsheet"),
        ("/archive/", "Archive"),
    )
}
```

This is an example of how I've done mine.

## 8) Indexes as a list of links or list of posts

Nikola allows you to categorize posts in a number of ways such as category, tags, archives, and authors. For each means of categorizing, an associated index page is generated so that viewers can see all available posts (*_PAGES_ARE_INDEXES = True*) or links associated to that category (*_PAGES_ARE_INDEXES = False*).

You can choose for these indexes to produce a list of the full posts (or [showing teasers instead of the full post](#short-blog-post-teaser-in-index-page)) or a list of links to each post. Depending on your needs, you can change any of the following index settings in `conf.py` to True.

```python
CATEGORY_PAGES_ARE_INDEXES = False
TAG_PAGES_ARE_INDEXES = False
ARCHIVES_ARE_INDEXES = False
AUTHOR_PAGES_ARE_INDEXES = False
```

This is what makes Nikola so customizable. For example, since there are less Categories, and you may have more posts under each category, you might want them as a list of links. Alternatively, with Tags there are usually more of them, so less posts under each Tag so you want a list of posts.

## 9) Enable a comment system

Because static sites do not have databases, you need to use a third-party comment system as documented on the official doc.

1. Sign up for an account on [https://disqus.com/](https://disqus.com/).
2. On Disqus, select "Create a new site" (or visit [https://disqus.com/admin/create/](https://disqus.com/admin/create/)).
3. During configuration, take note on the "Shortname" you use. Other configs are not very important.
4. At "Select a plan", choosing the basic free plan is enough.
5. At "Select Platform", just skip the instructions. No need to insert the "Universal Code" manually, as it is built into Nikola. Keep all default and finish the configuration.

In `conf.py`, add your Disqus shortname:

```python
COMMENT_SYSTEM = "disqus"
COMMENT_SYSTEM_ID = "[disqus-shortname]"
```

Deploy to GitHub and now the comment system should be enabled.

## 10) Deploying your website

My workflow is separated in two parts :

1. Github Pages
2. Netlify

### Github Pages

I decided to host my blog files on GitHub and use their free service, GitHub Pages, for deploying my blog on this address [https://mattioo.github.io](https://mattioo.github.io).

For doing that you will need to have a [GitHub](https://github.com/) account, and enable [GitHub Pages](https://pages.github.com/). Once you created your repository as explained for GitHub Pages, initialize GitHub in your source directory

```bash
git init .
git remote add origin https://github.com/<USER_NAME>/<USER_NAME>.github.io
```

The `conf.py` should have the following settings.

```python
GITHUB_SOURCE_BRANCH = 'src'
GITHUB_DEPLOY_BRANCH = 'master'
GITHUB_REMOTE_NAME = 'origin'
GITHUB_COMMIT_SOURCE = True
```

Create a `.gitignore` file with the following entries as a minimum. You may use [gitignore.io](https://www.gitignore.io/) to generate a suitable set of `.gitignore` entries for your platform by typing in the relevant tags (e.g., mac,`nikola`,jupyternotebooks).

``` txt
cache
.doit.db
__pycache__
output
ipynb_checkpoints
*/.ipynb_checkpoints/*
```

By using the `nikola github_deploy` command, it will create a `src` branch that will contain your contents (i.e., `*.ipynb`, `*.md`, and a `master` branch that will only contain your html output pages that are viewed by the browser.

```bash
nikola github_deploy
```

### Netlify extra steps

Because of [all these reasons](https://dev.to/eroberts/why-you-should-use-netlify-instead-of-github-pages-3on1) I wanted to use Netlify for deploying my blog with a custom domains, www.brainsorting.dev. I simply configured a trigger on Netlify to start building my blog when it detects any new push on my GitHub blog repository. It is as simple as said, everything kind of works out of the box and the service provided by Netlify has been very stable and giving me very good SEO statistics.

## 11) Archives

Nikola has many options for how you would display your archive of posts. I've kept it pretty simple on my end.

```python
# Create per-month archives instead of per-year
CREATE_MONTHLY_ARCHIVE = False
# Create one large archive instead of per-year
CREATE_SINGLE_ARCHIVE = False
# Create year, month, and day archives each with a (long) list of posts
# (overrides both CREATE_MONTHLY_ARCHIVE and CREATE_SINGLE_ARCHIVE)
CREATE_FULL_ARCHIVES = False
# If monthly archives or full archives are created, adds also one archive per day
CREATE_DAILY_ARCHIVE = False
# Create previous, up, next navigation links for archives
CREATE_ARCHIVE_NAVIGATION = False
ARCHIVE_PATH = "archive"
ARCHIVE_FILENAME = "archive.html"
```

## 12) Content Footer

I use the recommended license :

```python
LICENSE = """
<a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">
<img alt="Creative Commons License BY-NC-SA"
style="border-width:0; margin-bottom:12px;"
src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png"></a>"""
```

You might want to have a specific message (i.e., license, copyright, contact e-mail address) at the footer of every page and this is where to do it. In this case I've added a [Mailchimp](https://mailchimp.com/) link so that readers can subscribe to my page.

```python
CONTENT_FOOTER = '''
<center>
''' + MAILCHIMP_SIGNUP + '''
<br>
Contents &copy; {date} <a href="mailto:{email}">{author}</a> <a href="https://dev.to/mattioo"><i class="fab fa-dev" title="mattioo's DEV Profile"></i> </a> - Powered by <a href="https://getnikola.com" rel="nofollow">Nikola</a> {license} - favicon <a href="https://www.flaticon.com/">FlatIcon</a>
</center>
<br>
'''
```

## 13) Rendering math equations

I have enabled KaTeX because its prettier with the `$...$` syntax as thats more similar to LaTeX.

``` python
USE_KATEX = True
KATEX_AUTO_RENDER = """
delimiters: [
    {left: "$$", right: "$$", display: true},
    {left: "\\\\[", right: "\\\\]", display: true},
    {left: "\\\\begin{equation*}", right: "\\\\end{equation*}", display: true},
    {left: "$", right: "$", display: false},
    {left: "\\\\(", right: "\\\\)", display: false}
]
"""
```

## 14) Implementing Google tools

### Google search

I've enabled Google search form to search in my site.

``` python
SEARCH_FORM = """
 <form method="get" action="https://www.google.com/search" class="form-inline my-2 my-lg-0" role="search">
 <div class="form-group">
 <input type="text" name="q" class="form-control mr-sm-2" placeholder="Search">
 </div>
 <button type="submit" class="btn btn-secondary my-2 my-sm-0">
    <i class="fas fa-search"></i></button>
 </button>
 <input type="hidden" name="sitesearch" value="%s">
 </form>
""" % SITE_URL
```

### Google Analytics

Google Analytics can be added to the bottom of `<body>` to function.

``` python
BODY_END = """
<!-- Global Site Tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=GA_TRACKING_ID"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', '<YOUR GOOGLE ANALYTICS IDENTIFIER>');
</script>
"""
```

## 15) Customizing your blog

### Theme & Template customization

To create a new theme, we can use the following command which will create a new folder in themes called `brainsorting`. It is using the mako templating engine and the parent theme is `bootstrap4`. We don't necessarily want to create a theme from scratch, so we base it off the bootstrap4 theme (or whatever theme you want) and make the adjustments that we want.

``` bash
nikola theme --new=brainsorting --engine=mako --parent=bootstrap4
```

We can also copy over any templates from the parent theme where we want to make modifications by using the following command :

``` bash
nikola theme --copy-template=base.tmpl
```

If you want to examine all the components of the parent theme (i.e., bootstrap4 in my case), the following command will give you the path to the parent theme for you to explore.

``` bash
nikola theme -g bootstrap4
```

The full list of templates is shown below:

``` txt
.
├── authors.tmpl
├── base_helper.tmpl
├── base.tmpl
├── gallery.tmpl
├── index_helper.tmpl
├── listing.tmpl
├── pagination_helper.tmpl
├── post.tmpl
├── tags.tmpl
└── ui_helper.tmpl
```

For example if you want to make the nav bar sticky at the top, so that when readers scroll downwards, they can still access the menu bar, you need to update the `base.tmpl` file as shown below with the command sticky-top. To get the `base.tmpl` file in your template folder use `nikola theme --copy-template=base.tmpl`.

``` html
<nav class="navbar navbar-expand-md sticky-top mb-4
```

### Setting your favicon

Pick an icon and store it in the folder '/file/', then edit the conf.py as follows :

``` python
FAVICONS = (
    ("icon", "/brain.png", "128x128"),
)
```

### Tweaking the CSS

This is quite easy and can be done by dropping a custom.css document here `files/assets/css/custom.css`. This will be loaded from the `<head>` block of the site when built.

If you really want to change the pages radically, you will want to do a [custom theme](https://getnikola.com/theming.html).

### Files and Listings

This two folders are used to transfer any file or code file to the output folder (your generated website). By default, putting anything in the `files` folder will be available in the root of your website. Anything in `listings` or subfolder will be available in `output/listings`. This last folder allows user to view and download any code file you put in this.

### Date formatting

You can customize how the timestamp are displayed on your blog posts.

``` python
DATE_FORMAT = 'yyyy-MMM-dd'
DATE_FANCINESS = 2
```

I like to set it like this to have a more human friendly reading with dates displayed like "3 months ago". Hovering the date with the mouse display the exact date.

### Using mailchimp for user to subscribe

Mailchimp allows you to run e-mail campaigns and contact subscribers when you have new content on your site. See [Getting Started with Mailchimp](https://mailchimp.com/help/getting-started-with-mailchimp/) for more detailed instructions.

After you created your account you can create your signup form and get a code that looks like this one. Create a `MAILCHIMP_SIGNUP` variable in `conf.py` and paste this code :

``` python
MAILCHIMP_SIGNUP = """
<!-- Begin Mailchimp Signup Form -->
<div id="mc_embed_signup">
<form action="<YOUR MAILCHIMP IDENTIFIER>" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
    <div id="mc_embed_signup_scroll">
	<label for="mce-EMAIL">Subscribe</label>
	<input type="email" value="" name="EMAIL" class="email" id="mce-EMAIL" placeholder="email" required>
    <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
    <div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_3ffb6593478debd1efe5bf3e7_e432d28210" tabindex="-1" value=""></div>
    <div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
    </div>
</form>
</div>

<!--End mc_embed_signup-->
"""
```

### Loading bar

To make use of the [pace.js](https://github.hubspot.com/pace/docs/welcome/) loading library I added this code in the `base_helper.tmpl` file :

``` html
# below the <title> in the base_helper.tmpl file

{% if use_pace %}
    <script src="/assets/js/pace.min.js"></script>
    <link href="/assets/css/pace.css" rel="stylesheet" />
{% endif %}
```

I then activate it with in the `conf.py` settings :

``` python
GLOBAL_CONTEXT = {
    "use_pace": True,
}
```

### Remove .html suffix from archive.html

This is just a small annoyance, but by default, the archive is located in `/archive.html`. If you want it to be in `/archive/`, add the following lines to your `conf.py`:

``` python
ARCHIVE_PATH = "archive"
ARCHIVE_FILENAME = "index.html"
```

Remember to also fix the navigation links:

``` python
NAVIGATION_LINKS = {
    DEFAULT_LANG: (
        ("/pages/resume/", "My Resume"),
        ("/pages/cheatsheet/", "Cheat Sheet"),
        ("/archive/", "Archive"),
    ),
}
```

### Short blog post teaser in index page

The `index.tmpl` will generate a list of posts associated to the tag/category/year/author. This index can either be the entire post or post with just a teaser. To just show a teaser of the post, set `conf.py` as follows:

``` python
INDEX_TEASERS = True
```

Don't forget to write in your post file where is the end of the teaser, in Markdown or html or ipynb do like this :

``` markdown
<!-- TEASER_END -->
```

In reStructuredText, select the end of your teasers with:

``` rst
.. TEASER_END
```

If you are using teasers, the default is a Read more... link to access the full post. To make it more informative, you can have statements such as XX minute read... in `conf.py` as shown below.

``` python
INDEX_READ_MORE_LINK = '<p class="more"><a href="{link}">{reading_time} minute read…</a></p>'
FEED_READ_MORE_LINK = '<p><a href="{link}">{read_more}…</a> ({min_remaining_read})</p>'
```

## 16) Optimizing your blog

### Filters

I want to be sure that all the files on my blog are optimized (such as .html, .js, .jpeg, .png, .css) so I am making use of the filters functionality.

In my `conf.py` I have the following settings for FILTERS :

``` python
FILTERS = {
   ".html": ["filters.typogrify"],
   ".css": ["filters.yui_compressor"],
   ".jpg": ["jpegoptim --strip-all -m75 -v %s"],
   ".png": ["filters.optipng"]
}
```

For them to work I need to have `typogify`, `yui_compressor`, `jpegoptim`, and `optipng` installed on my machine. Those instructions are for mac using `homebrew`:

``` bash
brew install yuicompressor
brew install optipng
brew install jpegoptim
poetry add typogrify # or "pip install typogrify" if you don't use poetry
```

### Posting automatically on Medium

To publish your Nikola posts on Medium there is this [plugin](https://plugins.getnikola.com/v8/medium/).

You just need to install it with this command :

``` bash
nikola plugin -i medium
```

Add a `medium.json` file in your blog folder with a generated access token that you can get [here](https://medium.com/me/settings) :

``` bash
{
	"TOKEN": "your_token_here",
}
```

Then simply add the metadata `markdown : yes` in the blog post you want to publish on medium and run the command :

``` bash
nikola medium
```

### Posting automatically on Dev.to

To publish your Nikola posts on Dev there is this [plugin](https://plugins.getnikola.com/v8/devto/).

You just need to install it with this command :

``` bash
nikola plugin -i devto
```

Add a `devto.json` file in your blog folder with a generated access token that you can get [here](https://docs.dev.to/api/) :

``` bash
{
	"TOKEN": "your_token_here",
}
```

Then simply add the metadata `devto : yes` in the blog post you want to publish on medium and run the command :

``` bash
nikola devto
```

### Other plugin I didn't try

- **[similarity](https://github.com/getnikola/plugins/tree/master/v7/similarity) :** Find posts that are similar to the one being read. Requires install of Natural Language Processing packages such as `gensim`.

## Conclusion

That's it for the big tutorial. I hope I was precise enough for giving you all the tools to make a blog very much like you want it. Some part of that article were very inspire (if not copy/pasted for some stuff) from resources down below that helped me a LOT to understand everything I need to understand to reach a result that I like with my blog. My next step will be to automate as much things as possible, you'll know it in a new article when I will have achieve it 😄

_Google group to discuss about Nikola : [https://groups.google.com/forum/#!forum/nikola-discuss](https://groups.google.com/forum/#!forum/nikola-discuss)_

## Resources

[https://getnikola.com/handbook.html](https://getnikola.com/handbook.html)

[https://getnikola.com/getting-started.html](https://getnikola.com/getting-started.html)

[https://nikola.readthedocs.io/en/latest/manual](https://nikola.readthedocs.io/en/latest/manual)

[https://jiaweizhuang.github.io/blog/nikola-guide](https://jiaweizhuang.github.io/blog/nikola-guide/)/

[http://www.jaakkoluttinen.fi/blog/how-to-blog-with-jupyter-ipython-notebook-and-nikola/](http://www.jaakkoluttinen.fi/blog/how-to-blog-with-jupyter-ipython-notebook-and-nikola/)

[https://randlow.github.io/posts/python/create-nikola-coding-blog](https://randlow.github.io/posts/python/create-nikola-coding-blog/#theme-template-customization)/

### Themes

[https://themes.getnikola.com/v7/mdl/](https://themes.getnikola.com/v7/mdl/)

[https://hackerthemes.com/bootstrap-themes/](https://hackerthemes.com/bootstrap-themes/)

[https://bootswatch.com/](https://bootswatch.com/)