CyberSecurity // Azure // Frontend // Svelte // Cloud Native // Software // CyberSecurity // Azure // Frontend // Svelte // Cloud Native // Software

Building the blog

Cover for Building the blog


This blogging website has gone through quite a few iterations before I landed on what you currently see. I thought why not share the lessons learned, so others might benefit from this!


Image: Blogging!


Before we continue, I would like to give credit where credit is due. I took a lot of inspiration from two other tech related blogging sites:

  • : Tim is a colleague of mine and a great tech blogger! Took a lot of inspiration tech-wise for the blog.
  • : Simple but effective webdev blog, took some inspiration from the design. Since writing this blogpost, the design of their site has changed.

Frontend framework

My favorite frontend framework of choice is, as you might have guessed, Sveltekit . The component based approached, combined with file based routing and general simplicity really makes using Svelte a fun experience.


Image: Svelte

So it's probably no surprise that I went with Sveltekit as the frontend framework for this blogging site. I don't use any fancy external styling solutions. Just plain css written in each component file.

Converting markdown

Source of truth

When iterating over the architecture of this blogging site, the source of truth has changes quite a lot. At the start I used a online content management systems (CMS) called Strapi . While it worked greatly, I wasn't keen on managing yet another external system.

I moved on to storing my content in +page.svelte files in the routes folder. I also kept track of all posts and their metadata in a separate JSON file. This meant that I had to keep two systems in sync, and while this isn't really complex, it's just annoying. Storing each blogpost as a sveltekit page gave me great flexibility, as each page was fully customizable and could have a vastly different look, feel, or even embed other svelte components in it. However, this also meant that I had to keep maintaining all these things. A more general solution that would also work outside of my own environment was needed.

That's when I switched over to markdown files. The content folder of my repository now contains a bunch of markdown files which get transformed to html.

The filename dictates the route that will be used under the /blog directory. So a file called will result in a blogpost that can be read at /blog/001-my-first-post.

Svelte markdown

I've created an internal svelte api endpoint that spits out an array of posts which then can be used further down the line.

The endpoint runs on the server and uses fs to read all the files in the /content directory.

Front matter

When all the files are loaded, I pass them through Front Matter , which allows me to extract all the metadata from a markdown file. This way, I split the file into the metadata and the actual markdown that needs to be rendered.


At this point each post already knows his final url, so I can start collecting the viewcount. This information is stored in a Supabase table .

Supabase is used to store viewcounts

Image: Supabase is used to store viewcounts

To prevent constantly needing to fetch this data when I'm developing locally, I implemented a cache. Each viewcount fetch first checks if there isn't a local file containing the requested information. With a cache maximum age set to 1 hour, this greatly reduces the amount of calls I have to make to the supabase database.


Next up is generating a hyperlinked table of contents. By using Marked I'm able to iterate over all the tokens that my markdown file contains. Whenever I encounter a heading element I add this to a list, and this can then later on be used to generate the table of contents.

Svelte markdown

To actually render the markdown into html, I use Svelte Markdown . This library provides a component that accepts a string of markdown, and optional extra renderers to convert this markdown.

For example:

// SVELTE //

The content of the heading.svelte and code.svelte file overwrites the default rendering behavior. You can have a look at the default renderers on the Svelte markdown repo . I have customized a few of these, which can be found in the public repo of this blogging site .

This allows for some nice custom behavior. As an example, I've added some optional styling to image tags .

An image in markdown format is defined as


These custom rendering components allow me to pass some extra information via markdown.


This [small] tag is picked up by the image renderer and applies some additional styling.

It's an easy (but hacky) fix to customize images from within the markdown sourcefile. In the same fashion, I've replaced the default quote block with a note element. This means that the following markdown


is rendered like this:


Hi there, I'm writing a note

Comment section

At the bottom of each blogpost, there is a component that renders a comment section. This comment section makes use of Github Issues, and is powered by Utterances . Each post has a comment section

Image: Each post has a comment section

Users can login using their Github account. All comments are public, and can be viewed on the issue tracker on Github as well.


Hosting is done on Vercel . Integration with Github means that for every commit on the main branch, a new build is triggered.

This website is hosted on Vercel

Image: This website is hosted on Vercel

Build webhook

Additionally, we can also manually trigger this build process using webhooks. A simple HTTP post request to a specific project URL will start a new build. Because this site makes use of a static build process, I run this webhook daily. This means that external information such as the viewcount is refreshed and rerendered every time this hook gets called.

Open source

This entire website is open source, and can be viewed on Github . If you see a spelling mistake, content error or just a bug in the website, please do let me know!


If you want to start blogging, you definitely do not need to create a blogging platform from scratch. However, I had a great time building this blog in it's current form, and will probably keep iterating on the design and inner workings!