Site generation with C templates

2025-08-09

static site generator c template markdown

mite template
Mite Template

This is a mite template. It is HTML with C between <? and ?>.

What is mite?

mite is a static site generator written in C. It uses templates that are basically plain HTML files with embedded C code between <? and ?>. This lets you write your layouts as HTML while using C code for dynamic parts like inserting lists or including other templates. There are no dependencies or external tools. You just need a C compiler. The output is plain HTML files ready to be served.

Here’s a simple template snippet that dynamically generates a list of links:

mite template
Mite Template

You write mostly HTML with small embedded C blocks to insert dynamic content. No extra templating language, just plain C inside HTML.

Inspiration

I got nerd sniped by Tsoding's video Server-Side Rendering in C. The idea of templating with plain C hooked me.

xkcd nerd_sniping
Nerd Sniping (https://xkcd.com/356)

I had recently moved from Jekyll to plain HTML (explained here) and was using pandoc to convert Markdown to HTML. And I didn't want to use it anymore.

I figured I could write my own Markdown renderer. Markdown looked simple enough. It wasn’t. I built a markdown renderer of my own, which ended up taking more time than expected even for my limited needs.

After that, I started working on the templating engine.

Templating engine

The engine runs in two modes: HTML and C.
It starts in HTML mode. When it sees <?, it switches to C until ?>.

Example

example mite template
Mite Template
generated c
Generated C

The macros handle output, they append to the current output StringBuilder.
HTML bytes are stored directly, so that we dont have to mess with escaping.
When this generated code runs, it outputs this:

rendered html
Rendered HTML

Second Stage

The generated code is pasted into a new file site.c as the second stage in the process. It is compiled and run by the first stage.

Second stage has these responsibilities:

Construct a global state

Second stage creates fills a global struct for all the pages and templates to use. This is done by pasting the front matter of all the pages.

filling global state
Filling Global State

Construct the templates list with their details

We need to assign a function to the name of the template, heres where we fill this.

filling template list
Filling Template List

Render each page with its layout

First I was searching for "CONTENT()" in the templates and replacing it with the rendered HTML directly as a string.

This meant every page had its layout printed directly into it at build time. It worked, but it wasn't flexible. The layout had to be known in the first stage, meaning layouts couldn’t be changed dynamically in the page's front matter.

This made me rethink the architecture of the templating engine. Why was I manually searching and replacing CONTENT() ?

A template should be able to render multiple pages, so I cant just make the macro the lines that render the content.

Instead of printing templates everywhere, each template became a function. The function takes the page's content rendering function as an argument, so any layout can render any content at runtime.

render functions
Render Functions

At render time we find the correct template function and call it with the page and its content function.

rendering the page
Rendering The Page

And because the rendering happens at runtime (after parsing the front matter), the same templating system can be used not only in layouts, but also inside the markdown content itself.

INCLUDE macro

I used the same approach with includes. Instead of concatenating the included file as static HTML, we just call its template function. That means includes can be templated too.

An include template and a layout template are actually the same thing under the hood. We construct it the same, and we call it the same.

An include can even have nested INCLUDE() calls or a CONTENT(). (it really shouldn't, but why not?) It can even include itself, until we blow the stack.

RSS

You can generate rss.xml by creating rss.md and setting this files output like page->output = "rss.xml"; in its front matter. Take a look at this site's rss.md file.

Real world usage

This site is built with mite. You can explore its source code to better understand how it works: https://github.com/hanion/hanion.github.io

Source Code

Source is available on GitHub: https://github.com/hanion/mite