Static site generators are very convenient for smaller websites such as portfolio or personal/company blog. They are blazingly fast, easy to maintain, and do not require much backend development to run.

This article shows a common pattern used in static site generators that allows to separate a page's content from its view/template — while keeping a handy referrence to meta fields such as SEO title, description, post tags etc.

Node packages

  • Express - Node.js server / framework
  • Marked - a markdown parser and compiler.
  • Yaml Frontmatter - returns parsed yaml + string content in an object literal.
  • Twig - JS implementation of the twig templating language

Folder structure


How this works

First, let's take a look at this code snippet which outlines the basic logic in use.

// Home route
app.get("/", async (req, res) => {
  const data = await read("home");
  if (data) {
    res.render("home.twig", parse(data));
  } else {
    res.send("Error: Page doesn't exist");

// Get markdown file based on route
async function read(route) {
  return new Promise(resolve => {
    try {
      const data = fs
    } catch (error) {

// Parse data and convert markdown to html
function parse(data) {
  let frontMatter = yaml.loadFront(data);
  const html = markdown(frontMatter.__content);

  delete frontMatter.__content;
  frontMatter = { ...frontMatter, html: html };

  return frontMatter;

Whenever the home route is hit, we call an async function that does 3 things:

  • It looks for home/ folder inside data/ and reads the markdown file inside
  • If it finds one, it parses the yaml part and converts markdown __content to html
  • It renders the corresponding twig template with parsed data — home.twig

Now let's see what our markdown file — — looks like:

title: Some page title
description: Description comes along
img: featured-image.png
## This is an H2 tag
This is a lorem ipsum paragraph, it goes without saying...

As it can be guessed, the content between '---' is the yaml front matter and everything below that is simply markdown.

The above code is then used to populate different elements in our view — page.twig:

{% extends 'partials/base.twig' %}

{% block content %}
    <h1>{{ title }}</h1>
    <p>{{ description }}</p>

  <div class="content">
    {{ html }}
{% endblock %}

The complete code can be found here.