How this website is built

 
 
  • Gérald Barré

I often get questions about how this website is built. So, let's answer it in this post.

#The journey: From WordPress to static site generation

I've been blogging for more than 10 years, so I've updated my tools over time. Let me walk you through the evolution.

##The WordPress era

I started with WordPress hosted on a VPS. It's very simple to use at the beginning. You don't waste your time on technical stuff and you can start writing. Remember that this is the most important point: being able to publish content! However, there are some pain points (maybe it has changed since then):

  • Writing code blocks is painful
  • The text editor is not great for technical content
  • No global search across all posts, making it hard to mass-update content and create cross-post links
  • Reliance on plugins to extend functionality, which can lead to maintenance issues and security vulnerabilities
  • Difficult to create custom editor features without PHP expertise

##Custom solution with ASP.NET Core

So, I switched to a custom solution. I was also in a phase where I wanted to learn new technologies and had a lot of free time (what a great time it was!). I built a custom solution based on ASP.NET Core, with posts written in Markdown files (like many static site generators nowadays). I hosted it on a VPS. It was a great improvement: I could write my posts in Markdown, use VS Code to create and edit them, and enjoy great performance.

However, this setup had its drawbacks. I had to maintain the server, update the software regularly, and handle deployments. This was time-consuming and not particularly interesting. It also cost a few bucks per month for the VPS. You're also subject to downtimes, either because of the hosting provider or because of your own mistakes.

##Moving to static site generation

The previous solution was great, but I needed to reduce the maintenance time. At that time, static sites started to be popular and hosting solutions became more robust. So, I updated my solution to generate a static site. This wasn't a big change in the code; I only needed to iterate on all URLs, generate the HTML (using the existing code), and save it to disk. Then, you can deploy the files to a static file hosting service. I could have used an existing static site generator, but my existing solution was already working well and contained many validations on the content to prevent errors.

Over time, I simplified the code thanks to new .NET features and libraries. For instance, Blazor allows using the template engine without all the ASP.NET Core overhead.

To host the website, I started with Netlify. Back then, Netlify had frequent downtime. So, I also tried Azure Static Web Apps and Cloudflare Pages to easily switch between them when there was a downtime. They are now all great solutions. Since they're all free for my use case, I've kept all 3 of them as a fail-safe. If one of them is down, the other 2 are still up. I use Cloudflare as a CDN in front of the 3 services, allowing me to switch between them seamlessly if needed. GitHub Actions builds the site and deploys it to all 3 services automatically.

#Evolution of writing tools

The hosting and deployment strategy wasn't the only thing that evolved over the years. The tools I use to write posts have also changed significantly, always with the goal of making the writing experience better and more efficient.

##WordPress editor

In the early days with WordPress, I used the built-in web browser editor. It was functional but limited, especially for technical content with code blocks and formatting.

##Windows Live Writer

When I moved away from WordPress but still wanted a rich editing experience, I switched to Windows Live Writer. To handle code blocks properly, I created a custom extension for code editing that provided better syntax highlighting and formatting for code snippets.

##Custom Markdown editor

As I transitioned to Markdown-based content, I built my own custom Markdown editor in WPF (don't forget that I had time for fun projects back then!). This gave me complete control over the editing experience and allowed me to add features specific to my workflow and content structure. It supported features such as previewing the rendered HTML, managing metadata, and pasting images directly into the editor.

##VS Code with custom extensions

Eventually, I realized that VS Code was already an excellent text editor with great Markdown support. I created custom VS Code extensions to enhance the writing experience:

  • Scaffold new post: Quickly creates a new post with the correct structure
  • Content validation: Ensures metadata and formatting are correct
  • Editor helpers: Provides shortcuts and tools for common tasks
  • Custom blocks: Supports special content blocks used in my posts
  • Post references: Makes it easy to reference and link to existing posts

Recently, I've started incorporating AI tools into my writing workflow. I've created specific instructions for AI agents to help with various aspects of blog post creation, from structuring content to polishing the final text. This has been particularly helpful for generating initial drafts and ensuring consistent quality across posts.

#Key decisions and automation

##Automation features

I want to focus on writing content, not maintaining the site. So, I've updated the site generator over time to add more features, mainly to avoid errors and automate anything that can be automated:

  • Cover image generation: I use an HTML template and Playwright to generate cover images automatically. Before this, I used PowerPoint to generate cover images.
  • Link validation: All internal URLs in posts are validated at build time, so there are no broken links. I also validate that I don't remove public URLs to avoid breaking external links.
  • Detect TODO in post: Identifies any TODO comments in the post content and blocks deployment
  • Content organization: Posts are automatically moved to the right folder based on the publish date and title. This is essential when managing more than 640 posts.
  • Image and video optimization: The build process converts images and videos to multiple formats (AVIF, WebP, WebM, AV1) to create smaller files and provide modern formats with fallbacks.
  • HTML and CSS minification: The build process minifies HTML and CSS files to reduce their size.
  • Icon bundling: All icons are bundled into a single SVG sprite to reduce the number of HTTP requests.
  • Up-to-date dependencies: I use Renovate to automatically update the dependencies of the site generator. This keeps dependencies up-to-date and helps avoid security vulnerabilities.

##Removing comments

When switching to a static site, I also removed the comment feature from the blog. Static sites don't have dynamic features like comments built-in, so adding this functionality would require integrating third-party services like Disqus or utterances. This adds complexity, increases maintenance burden, and can negatively impact page performance. Beyond the technical considerations, filtering out spam is time-consuming, and very few comments are truly relevant. I prefer people commenting on social media. Also, I don't feel bad when I ignore a comment on social media. When it was on my blog, I always felt the need to answer every single one. This is another way to free up time.

Using a static site means losing dynamic features like search. To address this, I rely on Google, Bing, or ChatGPT for search functionality. That's why having a public website is useful: other companies provide the search engine for free. You can see this in action on the archives page.

#Technical implementation

The site generator is completely custom, built with the latest version of .NET (I update it every year). It reuses some excellent .NET and Node.js dependencies:

  • AngleSharp: For HTML parsing and manipulation, such as updating code blocks to use Highlight.js or adding tags to the head element
  • Markdig: For converting Markdown to HTML
  • YamlDotNet: For front matter parsing
  • Highlight.js: For syntax highlighting
  • Terser: For JavaScript minification
  • LightningCSS: For CSS processing

I've also removed all unnecessary dependencies to reduce the maintenance burden. For instance, client-side JavaScript has been replaced with code executed during the generation process. Syntax highlighting of code blocks is now part of the build process rather than happening in the browser. The only JavaScript remaining is to handle the "copy code to clipboard" functionality and rewriting URLs to use the canonical URL.

The build process takes about 20 seconds on my machine for all 640+ posts and pages, which is quite reasonable. It uses a cache to avoid unnecessary recomputation of unchanged images, CSS, JavaScript, and posts. For instance, converting images to WebP and AVIF formats is quite slow, so caching them saves a lot of time.

#Performance and SEO

I value performance highly, both for user experience and SEO. Therefore, I've implemented several optimizations to ensure the site loads quickly and efficiently. You can read more in this series of posts about optimizing web performance.

The results speak for themselves:

  • Page size: About 10KB per page, loading very fast
  • Lighthouse score: 100/100
  • SEO: All metadata tags are properly set for optimal search engine indexing

#Current costs and maintenance

##Financial costs

  • Domain name: $12/year
  • GitHub (code + actions): Free
  • Cloudflare (CDN): Free
  • Azure Static Web Apps: Free
  • Cloudflare Pages: Free
  • Netlify: Free

##Maintenance time

  • Update .NET once a year
  • Renovate automatically updates the dependencies. It automatically merges some safe dependencies. For other dependencies, I need to check the PRs once in a while. I have very few external dependencies, so it's not a big deal.
  • Small bug fixes or updates: about 4 hours per year

#Conclusion

This setup gives me the best of all worlds: fast performance, almost zero maintenance cost, excellent reliability with multiple hosting providers, and complete control over the functionality. The initial investment in building a custom solution has paid off over the years, and the automation features save me significant time when managing hundreds of posts.

The journey from WordPress to this current setup took more than a decade, but each step was an improvement. The key lessons are:

  • Start simple: WordPress was perfect for getting started
  • Iterate based on pain points: Each migration solved specific problems
  • Invest in automation: Time spent on automation pays dividends in the long run
  • Keep it maintainable: Fewer dependencies and more automation mean less ongoing work

If you're building a blog or website, you don't need to build everything from scratch like I did. But understanding what's possible can help you make better decisions about which tools and platforms to use for your specific needs.

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub