Migrating blog to a static site generator - 11ty versus Astro

For a few years I was using a Python/Django web application to run my site - rk.edu.pl. It's a mix of Polish and English tech/programming content.

I wanted to update it and change it to solve some of my problems. For one, I wanted to have my content in a git repo, so it's easy to synchronize between the server and localhost. Managing database dumps and pulling media files from a server is more time-consuming and annoying.

Next thing was to make it as fast as possible, especially geographically when writing in English and having visitors from around the globe. The existing site is already fast but not as fast as it could be and the further from the Europe server location it gets slower.

Lastly frontend and webdev technology moves ever forward, so I was missing some cool new technology on my website. No optimized images, no dark/light mode, and few other things.

What's new?

I'm a big fan of single-column layouts but this time I did experiment with a side menu to showcase categories/content a bit more, especially when the old one had to be two-level to fit everything. I also preserved the double-border style from the original. Looks good and it seems to get the job done. I'm not a graphics designer...

Initially wanted to use thumbnail enlargement JS widget (one click to enlarge to full width by replacing SRC to original image link, second click to open original image in new window) but with the picture tag I added and WebP/AVIF thumbnails things got bit complicated and I skipped that for now. Will probably re-add when AVIF lands in MS Edge by default.

Lighthouse is my friend. This is some sort of showcase, so I wanted to optimize the site as much as possible. Making Prism styles pass contrast requirements was quite the challenge.

ToDo list

I'm planning to import some of my recent English content here, make permanent redirects on the old page, and use that website solely for Polish content. Internet says pages that turn into permanent redirects can/should stay in the original site sitemap for a while so that's what I've done for now.

The bigger part is making a simple browser editor for content files. I want a simple editor that will open paragraphs in a textarea and allow writing/quality control with the help of Grammarly. SSG integrated with some services like that could be really handy.

Picking a static site generator

As a Python developer I did look at the Python options like Pelican but wasn't that impressed. I wanted a clean static content generator that has a clean and controlled output. That's why I started looking at Astro.

I want to focus on Astro and 11ty here pointing out some good sides as well as things that I found missing or annoying. Note that those are ramblings of someone rather new to the frameworks!


Astro looks really well, the documentation is excellent and the development/social media seems to be all-time high. I liked the structure and how it works, the principles. I made a proof-of-concept website in Astro and I did find a few oddities or missing features as well.

Astro optimizations like the explicit hydration, local/global styles, and (new) image optimization component are really good. Not to mention making my own components is straightforward, at least for me with Vue/Ember experience. I did re-do some of my Django presentation components in Astro quite quickly.

The framework also supports content collections where you can use the filesystem as a sort of a database - list all or latest content pieces, validate the structure, and so on - but only for Markdown.

On the downside there are few oddities in Astro. It's inspired by JSX so it likes to have HTML inside JavaScript and lack block structures.

Let's start with the lack of block structures. For example code highlight component for HTML looks like so:

<Prism lang="js" code={`
many lines of code here
`} />

Like who does a code block without a block? Even their markdown version is a block version. And if your code looks like invalid JS/Astro code it will break the site.

You can make a block version of such a component rather easily:

import { Code } from "astro/components";

export interface Props {
	lang: string;

const { lang } = Astro.props;
let codeBlock = await Astro.slots.render('default');

<Code code={codeBlock} lang={lang} />

If you are familiar with Nunjucks, Handlebars, Jinja, Django templating system, and every other implementing similar syntax you would expect a clean block version of conditionals. JSX style is different and I don't like things like:

{conditional ? <a href="#" title="some HTML" target="_blank">
    Look, HTML here
    </a> : <p>Nope, the conditional failed...</p>}

Astro also lacks template code blocks but it does offer slots. The problem is that current page variables (props) have to be passed from child templates to parent templates making it messy when you have multiple levels of template inheritance:

<Layout title={Astro.props.title}>
        <p>some html</p>
        <p>other html</p>
        <p slot="footer">Look, a side effect/nasty named slot usage</p>
        <Fragment slot="header">
            <meta something="pong" />
        <p>Even more HTML</p>

It can be cleaned a bit by ignoring unnamed slots and using only named slots, but it's still a bit dumb:

<Layout title={Astro.props.title}>
        <Fragment slot="content">
            <p>some html</p>
            <p>other html</p>
        <Fragment slot="footer">Look, a side effect/nasty named slot usage</Fragment>
        <Fragment slot="header">
            <meta something="pong" />

If you use HTML pages the frontmatter will be as Astro.props, while when you use Markdown then the interface changes to Astro.props.frontmatter. One template can't handle HTML and Markdown content without handling two different interfaces.

When writing an article the start of the file will be full of imports, possibly some code:

import { Image } from 'astro:assets'
import Layout from '../../layouts/Article.astro';
import RkThumb from '../../components/RkThumb.astro';

import benchopenarena from "../../assets/python/bench-openarena.png";
import laptoptempendcpufreq from "../../assets/python/laptop_temp_end_cpu_freq.png";
import coverPhoto from "../../assets/python/fancard_gelid1.jpg";


<Layout title="Rozkład chlorowcopochodnych w impulsowym wyładowaniu barierowym" coverPhoto={coverPhoto}>
    <Fragment slot="content">
        <p>Some content here</p>
        <Image src={benchopenarena} width="600" alt="something" />
        <RkThumb src={laptoptempendcpufreq} alt="something" />

This could be cleaner if we could programmatically import images inside our own components from given paths. Astro requires images as imports for its image optimization system and to make thumbnails specifying only width or height instead of both. The same thing - the layout tag can be replaced by specifying a template in the frontmatter like in 11ty.

Also the image optimization will pass to the final site the original images as well, even when not used. This makes the build bigger for no good reason. Aside from that, all media end up in one folder. There could be some edge cases where one folder with a few thousand of files lags out a file manager or a filesystem on a low-end server.

In short my Astro wish-list:


Eleventy is a similar, yet different SSG. It's similar to its clean and controlled output, but it differs as it does not do a lot of optimizations Astro does out of the box. It's up to the developer to set up the SSG as he/she likes it.

On a top level both SSG offer similar features when it comes to SSG basics. There are more and more differences when you go deep down or to SSR but I'll skip this here.

When it comes to static content 11ty handles HTML much better. Frontmatter is here and is consistent across file types. You can easily build a template structure, extend blocks, and so on. Also, content collections are supported out of the box and they aren't limited to separate folders with markdown files. There is less structure to it but it's way easier to use and as I don't use Markdown I can actually use it.

Initially I was planning to use Astro, I had a POC project running locally, yet I found those annoyances listed above, so I did a quick re-look at alternatives and gave 11ty a second try. Initially, I rejected 11ty as I found it unclear to what it offers.

So let's start with that. 11ty comes with no initial configuration and the Getting Started and introduction is severely lacking any kind of an actual showcase. Make a file, run 11ty, done...

Actual developer will want to see features and will want to see what quality, style, and ease of use a project offers. 11ty introduction in this regard is trash. The only reason I started making POC projects with 11ty was because I watched a few YT videos from different authors and picked up things I needed/expected to have.

The documentation isn't bad... but it's also far from perfect. It feels like a mix of low-level reference and a bit of show-off and kind of confusing lists of supported template systems. I wanted to see how 11ty handles images, I went to the image plugin documentation page and I got some JS code on start, then WebC examples, then some more JS code and then a Nunjucks/HBS component example. Astro has a component, showcases it, it works great, and then you have a similar lower-level API. 11ty page lacks clarity and tries to support/show too many variants at once.

Most of best 11ty content is in YouTube videos and on third-party sites like 11ty.recipes. The main site could use some improvements.

In short my 11ty wish-list:


I hope you liked my Case study of migrating a blog from Django to 11ty and how the software stack decision-making process looked like. If you have any suggestions, or ideas on how to improve the site feel free to share them.

Comment article