Ali Naqvi

Back

The idea of having a website has long enticed me. I take a lot of notes and take great care to keep them well-organized. Writing useful ideas down has the obvious upside of reliable retrieval of information compared to human memory. I also find that writing helps me sharpen my thinking on any given topic. I largely agree with Holden Karnofsky’s Learning by Writing. It is so often the case that an idea makes perfect sense in my head, but when I start writing it down, there turn out to be glaring holes in my understanding. Or at least, it reveals various assumptions that had been implicit.

So having personal notes is great. But many of them deserve to be out in the open, for anyone to discover, learn, and criticize. This is how knowledge grows.

One common idea I encountered and sometimes believed was that having your own website is akin to reinventing the wheel. Why not use already established platforms like Medium or Substack? The answer is that while those platforms have their use cases, having your own website gives you extremely fine-grained control over everything. It might not be for everyone to create your own website from scratch. But there are gradations of the level of control that one may want. There are countless services available these days that can help you “spin up” and host a website.

When one thinks of starting a blog, often the first thing that comes to mind is WordPress. It was no different for me. But the option that comes to mind first is hardly ever the best option. So I started researching my options. While my coding knowledge is rapidly growing, especially in lower-level programming, I have to be strategic about what I spend my time learning.

The full spectrum of blog-building tools from low-level to high level

Level 1: Pure Code

At the fundamental level, all websites are HTML, which gives structure to the content you see, styled with CSS. JavaScript is used to manipulate the HTML and CSS to add interactivity and dynamic features. So HTML, CSS, and JavaScript are sent by the server and received by the browser, which renders the website that you see. Note that the JavaScript sent to the browser is called “client-side JavaScript” (JavaScript has applications that are far wider than just this) and this is only done in case something interactive or “dynamic” needs to happen on the website without reloading the page (without asking the server to send the HTML of the whole page again), e.g., the top bar of this website is dynamic (it appears on upscrolling and disappears on downscrolling). Here’s some client-side JavaScript for you to play with:

I am dynamic!

One can theoretically build a website from scratch with just vanilla HTML, CSS, and JavaScript. However, in practice, building something similar to this website would be extremely time-consuming. It would be difficult to manage content and it would be hard to scale (every post is a new HTML file or requires complex JavaScript). There are some limited use cases for this approach: Learning web fundamentals, very small personal sites where tinkering is the goal, digital gardens built piece-by-piece.

Building one’s website from scratch with this approach would really be reinventing the wheel. In programming and computer science, layers of abstraction exist because the lower level functionality becomes standardized. Unless there is a specific need for lower-level control, we abstract away (automate) the repetitive stuff so that we can build larger, more complex things.

Level 2: Frontend Libraries/Frameworks

The next layer is to use libraries and frameworks to structure your code. With frameworks like React.js, Vue.js, Svelte, and Angular, you can create reusable components. For example, you can write HTML for a “button” and create a Button component. This way, every time you want to insert a button in your web page, you just use the Button component instead of repeating all the HTML that went into creating the button. You can even pass it some properties so that one Button component can be used to refer to different kinds of buttons (e.g., colors, size, text, etc.). This provides much better code organization.

Creating a component is just like writing a function in a programming language once, and then simply calling that function (with specific parameters if needed) to perform that action as many times as needed, without needing to worry about the code and inner workings of the function. While this significantly reduces the development time of the frontend, you still need to build the “blog” functionality yourself (routing, fetching data, rendering Markdown, etc.). Use cases for this level include building highly custom web applications where a blog is just one part, or as the foundation for tools in the next level.

Level 3: Static Site Generators (SSGs) & Meta-Frameworks

These tools take your content (often Markdown files), apply templates, and generate static 1 The "static" in static site generation (SSG) refers to the fact that the entire website is generated ahead of time into plain HTML, CSS, and JavaScript files. When someone visits your site, the web server simply delivers these pre-built files as-is, without executing any code on the server for each request (e.g., for fetching data dynamically from a database). HTML, CSS, and JavaScript. This way, your focus shifts more to content, instead of the HTML, CSS, and JavaScript code. You still have a lot of control over how the translation from Markdown to HTML (and CSS and JavaScript) takes place. Tools in this layer include Next.js, Astro, Gatsby, Eleventy (11ty), Hugo, and Jekyll.

These tools are mostly developer-focused but differ vastly from each other and the learning curve varies. For example, it is possible to quickly spin up a website with Hugo (using a theme that someone else built) with very little coding knowledge. Next.js, on the other hand, is a very developer-focused but extremely powerful framework built on top of React.js (previous level), and provides not only static site generation (SSG) but also server-side rendering (SSR) capabilities. Next.js has quickly become the go-to choice for building modern websites that require a range of complex features. However, it can be overkill for mostly-static blogs.

In this level, you can write your content directly into text files (most often Markdown files). But since this can be inconvenient and lacking word-processing features, a headless CMS is a popular choice to manage content. These systems manage your content (posts, authors, categories) via a user-friendly interface and expose it to the site generators through an API, decoupling content management from the website’s frontend. Popular choices for headless CMS are Strapi, Contentful, Sanity.io, Payload CMS, WordPress (used headlessly).

Level 4: Traditional/Monolithic CMS

All-in-one systems that provide an admin interface for content management and render the frontend of the website using themes and templates. Often database-driven. Examples include WordPress.org (self-hosted), Drupal, Joomla, and Ghost (self-hosted option). Such systems require minimal coding and provide a lot of features out-of-the-box (or via plugins). Performance and level of customization can vary. Plugin/theme bloat or security issues are a risk if not managed appropriately.

Level 5: Hosted Platforms & Website Builders

These are commercial services that bundle the software, hosting, maintenance, security, and often a visual drag-and-drop editor into one package. This is the easiest way to get started with minimal technical knowledge. Examples include WordPress.com (hosted), Wix, Squarespace, Blogger (Google), Ghost (Pro-hosted), Medium, and Substack. The obvious downside is limited design/customization beyond the provided templates. Vendor lock-in can also be a big issue (you might not fully “own” your site’s underlying code or have full data portability). Can also become expensive with higher tiers/features.

Learning web fundamentals

Before I could choose a tool to build my blog, one thing was obvious to me: I should get more familiar with the fundamentals of the web, both because it will help me build a better website, but also because it will be invaluable in my software engineering career, no matter which specialization I end up choosing. So I dove into learning the basics such as understanding the syntax and usage of HTML, CSS, and JavaScript. But I promised myself not to get bogged down into the fundamentals so much that it prevents me from building any higher-level things (thankfully I was already familiar with the phenomenon of Tutorial Hell).

With that in mind, I went through a set of tutorials. Including freeCodeCamp’s Responsive Web Design course to get familiar with HTML and CSS, javascript.info to learn JavaScript, and various parts of the MDN Core Modules to sharpen concepts about frontend development. I went through these at varying speeds and often skipping or abandoning when I felt bogged down or the learning slowed down. The most important thing in my view is to get up to speed with enough of the basics to start building something. Then building that anything is a better teacher than passively consuming a large number of tutorials. And by basics, I don’t necessarily mean that you have to start with the lowest-level explanation in terms of the hierarchy of complexity. After all, we don’t need to know all the facts about the microelectronic structure underlying our computers in order to use the computers proficiently and productively. To “understand” something is very different from merely learning facts about something. “Understanding is about coherence, elegance, and simplicity, as opposed to arbitrariness and complexity,” as David Deutsch put it 2 Chapter 1 of David Deutsch's book The Fabric of Reality. . It is about making meaningful connections between the different levels of abstraction, and being able to answer why things are the way they are, and the kinds of things that must be happening, without necessarily being able to recite all the facts.

In this case, this process consisted of understanding what websites really are (renderings of HTML, CSS, and JavaScript), how they are served (sent from the server to the browser), how they are hosted (choosing where to serve from), what kinds of tools are available to create different kinds of websites, which of those tools are most widely used and why, etc.

Choosing a website-building tool

After gaining a reasonable understanding of the fundamentals (how the web works) and thinking through the full spectrum of tools as laid down above, it was time for me to choose. The full spectrum didn’t simply lay itself out in my head as it is listed here. There were naturally a lot of uncertainties and gaps in my understanding of the landscape. Not to mention that there was a lot of conflicting advice out there.

Reddit meme about how most advice on the internet is useless because it does not take your needs into account

The best way to eliminate those uncertainties was to experiment. Actually getting hands-on, as opposed to reading people’s opinions about it 3 Reading people's opinions about something is definitely useful to get a broad picture. LLMs (and tools like RedditScout.com) are also of great help in this step. , is often the best way for me to learn about and understand a topic. I went through official tutorials / quick start guides of various tools/frameworks mentioned above (e.g., React, Next.js, Astro, Hugo).

I seriously considered all levels from level 2 and up since they all have their pros and cons. I wanted to have as much control (ownership of data as well as design) over my website as possible. But I wanted to balance that with how much time investment will be required to learn and manage that. Also, I know that my needs will change in the future and my knowledge will grow, so I needed scalability and portability (ability of the website to grow and transform, or even move to another framework if needed).

I created a list of features that I would ideally like in my website (not necessarily immediately, but at some point in the future). I wanted the freedom to be able to implement features like customizable dynamic table of contents, progress bar that updates on scroll, system for searching and filtering posts, AI narration of articles, etc.

For these reasons, the tools in level 4 and 5 were much less attractive. React and Next.js were the most powerful options (that have become the leading choice in the industry by professional web developers). If I was sure I wanted to become a web developer or if my needs were more complicated than a blog, I would certainly choose the React/Next.js stack. However, the learning curve for them is notoriously steep. For someone completely new to web development like me, at the start there would be all sorts of bugs, performance issues, and possibly even security vulnerabilities in the website. Also, Next.js shines when it comes to dynamic websites with complex features. It was designed for building full-blown web applications, not with focus on static site generation, which is where I wanted to start.

So Next.js is something I will keep an eye on in the longer term. But for the time being, I turned my attention to tools that focus mostly on static site generation. Hugo and Astro are by far the most sophisticated and modern in this aspect (largely superseding the pioneering tools like Jekyll and Gatsby, although they still have their idiosyncrasies and use cases). Hugo’s primary selling point is its superior “build speeds” (the time it takes to generate the HTML from the input Markdown files and configuration files) as it is written in Go. But I didn’t understand why that aspect is so hyped. Maybe build speed is important when you have hundreds or thousands of pages on your website (like documentation sites). To me, it is a very minor aspect in the broader decision, when there are so many other deciding factors involved. Hugo mainly excels as building static sites, and does not make it straightforward to add client-side JavaScript for dynamic features. It does have numerous themes available to start with. But customizing them is not easy, as you’re forced to write complex Go templates or hacky JavaScript workarounds. Hugo simply does not have “highly customizable websites” as its selling point. But that’s exactly what Astro offers.

Astro is a relatively new player, but one that has quickly won the hearts of many, due to the high utility-to-complexity ratio that it offers. Astro, like React/Next.js, is component-based, but unlike Next.js, it is “framework-agnostic” (not tied to a single framework), i.e., it allows you to use components from any popular frameworks (React, Vue, Svelte, etc.) or no framework at all. It also ships zero JavaScript to the client by default, but this behavior can be altered in a piecemeal way. So by default, all the “input files” like Markdown files and code/configuration files are used to generate purely static/HTML files, but “partial hydration” can be introduced, such that there are “islands” of dynamic content (client-side JavaScript), while the rest of the site remains static. This paradigm offers an incredible combination of performance and flexibility. The learning curve is also not as steep as Next.js. For beginners starting with any of these frameworks, it is usually a good idea to start with a theme (or “starter templates”) that someone has designed using that framework, and then modify that theme to suit your needs. All the mentioned frameworks have many themes available (Astro, Hugo, Next.js), thanks to the generous contributions of the community members. I also found Astro themes to be much easier to heavily modify (and even mix and match) than others (especially Hugo).

Learning and navigating through Astro

Once I had chosen Astro, it was time to start building. As mentioned, choosing a theme and building upon it is usually a great way to kick-start this process. I scoured through the hundreds of themes that are available, to find something that has some of the features and aesthetic that I want, so I can build upon it. Some of the themes that fit the bill were Astro Micro, Astro Pure, Nordlys, and Webtrotion. After trying all of them out and testing and tinkering with them, I decided to use Astro Pure as the base of my website, and then heavily modify it. The generous creator of the Pure theme has added lots of useful components to it (e.g., MediumZoom, QRCode, Aside, Tabs, etc.), that can be used not only in the theme, but also in other Astro projects.

One important criterion for making such decisions is the breadth and depth of the available documentation. Astro itself naturally has extensive and high-quality documentation. But the Astro Pure theme is also reasonably well-documented.

Just like I went about understanding the fundamentals of the web (see above), I went on to seek to understand how Astro works. I learned about the syntax of .astro files, project and directory structure, configuration files, deployment options, Markdown and content collections, etc. At the same time, I kept deepening my understanding of HTML and how to style the HTML using CSS (especially learning Tailwind CSS) and to manipulate its structure using JavaScript and TypeScript.

I made various changes to the Astro Pure theme. This included changing the overall aesthetic by using different colors, fonts, roundedness of edges, etc. I also created a system for “topics” instead of “tags” and made the topics page feel much more dynamic (while remaining fully static). That said, as it stands, the structure of my blog is still mostly based on the original codebase of the theme, instead of my modifications to it. The blog will naturally continue to evolve and new features will be added as time goes on.

Choosing a content management system

Once I was happy (for the time being) with the structure and look of the website, I focused my attention on thinking about the content management system that I will use to write my content. In the past, I used many note management systems such as Notion, Obsidian, OneNote, and Evernote. Notion provides a very user-friendly yet powerful interface to create complex pages, which can be easily exported as Markdown files. Notion can technically be used as a backend to generate the Markdown files. But it would add a lot of overhead and intricate setup to get it to work seamlessly, since Notion is cloud-first and all your notes are saved first and foremost on the cloud instead of in your local file system. Obsidian, on the other hand, is slightly less user-friendly, but works mainly with your local file system. It also has a thriving plugin ecosystem that provides powerful tools such as Git integration, frontmatter support, AI-powered spell & grammar checking, etc. What’s more, Obsidian has a great mobile app with which you can easily make and sync changes on the go.

Obsidian seemed great for almost all my needs, except there was no way for me to seamlessly integrate it into my workflow. I do almost all my development on VS Code running on WSL (Windows Subsystem for Linux). So the local copy of all my Astro code (and blog posts as Markdown files) is located in the WSL file system instead of that of Windows. Obsidian installed on Windows unfortunately does not integrate well with WSL (and installing the Linux version of Obsidian inside WSL results in poor performance). So if I were to use Obsidian, I would have to set up additional syncing mechanisms (e.g., Git, Dropbox, or symbolic links) to synchronize between Obsidian on Windows and Astro on WSL. Additionally, Obsidian’s support for MDX is limited, although this was not a big factor in my decision for my choice of content management system.

By the way, I love VS Code. I think it is one of the best pieces of software ever developed. It is extremely versatile and works with so many use cases and development styles (I apologize to Neovim or JetBrains users who are clenching their fists right now). So why not try writing Markdown directly in VS Code? I decided to give this a try after I read that quite a few people are doing this. I was surprised by how powerful a Markdown editor it can become with the right setup. VS Code has some built-in support for Markdown and offers features like dynamic previews and document outline. But as always, there is an extension for everything in VS Code. The Markdown All in One extension is a must-have. It adds a ton of features like keyboard shortcuts (bold, italics, lists, etc.) and convenient commands such as inserting tables, links, images, etc. Markdown Table is another key extension that makes it very easy to edit and navigate tables (Tab to move around and commands for inserting and moving rows/columns, etc.). Front Matter CMS extension provides a powerful CMS without ever having to leave VS Code (I mainly use it to manage my required and optional frontmatter fields, such as tags, preview images, publish date, etc.). Code Spell Checker provides basic spell checking, and this is useful generally when coding, not just with Markdown content. I am still experimenting to find the best tool for high-quality spell and grammar checking. When I need to edit some content on my iPhone, things get a little less seamless. I use the extremely well-designed app Working Copy as a Git client. It has a decent built-in file editor too. If at some point in the future I need to do heavy editing on the phone and need a more user-friendly interface, Obsidian for iPhone can also be used in conjunction with Working Copy. So here is a brief summary of how Obsidian and VS Code compare as CMS for my purposes:

FeatureObsidianVS CodeWinner
Frontmatter Management✅ Excellent✅ Excellent🟦 VS Code (slight edge)
Spell & Grammar Checking (AI)✅ Good✅ Excellent🟦 VS Code
MDX Support❌ Limited✅ Excellent🟦 VS Code
Rich Markdown Editing & Tables✅ Excellent✅ Good🟪 Obsidian
Workflow & Syncing Ease (WSL)❌ Friction✅ Excellent🟦 VS Code

Concluding thoughts

All in all, it took me just a month or two to go from “HTML and JavaScript are nothing more than words to me” all the way to getting this website up and running, as I wanted it to be. I learned so much in the process and will continue to do so. It would not have been possible at all, if not for the wonderful and generous people who have developed these amazing tools (free or paid) to make others’ lives easier. Shout-out to the creators/developers of all the tools and tutorials that I mentioned (Astro, Pure theme, VS Code extensions, JavaScript.info, and so much more) or neglected to mention. This culture of standing with each other as we solve intellectual challenges and make collective progress is one of the main reasons I decided to pursue a career in software engineering.

How I built this website starting with no web dev experience
https://alimnaqvi.com/blog/this-website
Author Ali Mohsin Naqvi
First published on April 6, 2025

Some other posts you might like:

All posts