JF's Dev Blog

Django, Vue, and other things, too

Rebuilding My Blog with Pelican

Welcome to my new blog! While it may look pretty well the same as it has for the past 11 months, everything else has changed.

The first iteration of this blog was powered by Wagtail and the Puput blog app with a PostgreSQL database, a Redis store for caching, and Nginx to serve it. That’s a hefty stack for such a simple website.

This version is a static site generated with Pelican and served by Nginx. No Postgres or Redis required.

Why Pelican?

The short answer: I wanted to write articles in Markdown and Pelican (along with its plugins) provides every feature I need.

For the long answer, I’ll dig into three key benefits I get from Pelican:

  1. Simplified writing experience
  2. Improved page load times
  3. Extensive plugin collection

Simplified Writing Experience

Since this is a blog about development, I’m persnickety about how code appears in these articles. Code blocks need to be:

  • Server-side highlighted for fast page loads
  • Syntax highlighted using ayu colours to match the site’s colour scheme

The previous iteration of this blog used Wagtail’s rich text editor, Draftail. Draftail is a full-featured editor, and while it does support code blocks, code blocks aren’t included in Wagtail’s version of Draftail.

As a result, I had to roll my own solution for code blocks. (This one did syntax highlighting client-side, so I opted not to use it). I used Wagtail’s StreamField along with a custom code block to give me Pygments-highlighted code with an ayu colour scheme I put together:

class CodeBlock(blocks.StructBlock):
    """
    Code Highlighting Block
    """
    LANGUAGE_CHOICES = (
        ('python', 'Python'),
        ('htmldjango', 'HTML + Django'),
        ('html', 'HTML'),
        ('css', 'CSS'),
        ('sass', 'SASS'),
        ('javascript', 'JavaScript'),
        ('typescript', 'TypeScript'),
        ('text', 'Plain Text'),
    )

    code = blocks.TextBlock()
    language = blocks.ChoiceBlock(choices=LANGUAGE_CHOICES)

    class Meta:
        icon = 'code'
        form_classname = 'code-block struct-block'

    def render_basic(self, value, context=None):
        src = value['code'].strip('\n')
        lang = value['language']

        lexer = get_lexer_by_name(lang)
        formatter = HtmlFormatter(
            style=AyuStyle,
            noclasses=True,
        )
        highlighted_code = highlight(src, lexer, formatter)
        return mark_safe(highlighted_code)

That block did the trick. As I published a few articles, I found that writing in rich text slowed me down. I missed using Vim commands, and I missed being able to see my changes in real time like I can with Visual Studio Code’s Markdown preview.

I also knew from using MkDocs that Python-Markdown’s CodeHilite extension uses Pygments for its syntax highlighting. When I saw that Pelican uses Python-Markdown, too, I was sold.

Now writing code blocks that meet my needs is as easy as:

```python
# Python code goes here
```

Improved Page Load Times

I’m always chasing faster page load times. While building my Django-backed blog, I made sure to use template fragment caching, queryset caching, and custom CSS (instead of a heavy framework) to keep page loads zippy.

The Wagtail/Django version’s page load time was respectable enough to earn a 99 PageSpeed score.

Note

These page load speeds are throttled to emulate a spotty 3G/4G mobile connection. For more information on how Google PageSpeed Insights calculates mobile page speed scores, see the lighthouse throttling docs.

Wagtail blog PageSpeed score

But when I check my PageSpeed score, here’s what I want to see:

PageSpeed score 100

After all, how credible would my thoughts on web development be if I couldn’t build a performant blog?

The Pelican version got me that extra point thanks to its faster load times:

Pelican blog PageSpeed score

The overall PageSpeed load is 35% faster, and it’s lightning fast in the browser. Serving Pelican-generated HTML shaved around 100ms off page loads, and I’ll write another article about how else I sped up this version of the blog.

Extensive Plugin Collection

Pelican has a wealth of plugins in its pelican-plugins repository.

This blog makes use of three plugins.

assets Plugin

This plugin integrates webassets, which is a web asset processing library. I use it to convert SASS into compressed CSS using libsass-python.

If you scroll to the bottom of the post, you’ll see what the related posts plugin does. By adding related_posts metadata to this article, I can specify whether to show specific articles (by specifying slugs), show no related articles, or show articles with the same tags (this is the default behaviour).

sitemap Plugin

The sitemap plugin generates a sitemap. I’ll let Google define sitemap:

A sitemap is a file where you provide information about the pages, videos, and other files on your site, and the relationships between them. Search engines like Google read this file to more intelligently crawl your site.

An accurate sitemap helps ones of people find this blog (almost) every day:

Google search performance chart

Wrap-up

I’m happy with my move to a static site. Writing articles in Markdown, storing articles in git, and serving pages faster were worth the effort. I’m still a big fan of Wagtail, and I’ll continue to use it in other projects. For a simple site like this, however, Pelican fits the bill.