I tried a few blogging platforms before building this one. I started with Hashnode, but it feels like the project isnāt really maintained anymore. When I tried sharing posts, they wouldnāt fetch proper SEO data, which was pretty annoying. Mediumās markdown support is just⦠not great. Also considered dev.to but Iām not a fan of the interface, plus the overall quality of posts there isnāt really my vibe (not that mine are masterpieces, but you know what I mean). Iām actually not sure if I want to keep going here but just wanted to build something. So I built this one with Astro. Hereās how it works and why I made these choices.
System Overview
Letās start with the big picture, how this blog fits into the broader ecosystem:
graph LR
subgraph "External Actors"
Reader[Blog Reader]
Author[Content Author]
SearchEngine[Search Engines]
end
subgraph "Core System"
Blog[Threads of Thought Blog]
end
subgraph "External Systems"
GitHub[GitHub Repository]
GitHubPages[GitHub Pages]
Giscus[Giscus Comments]
GoogleFonts[Google Fonts]
RSS[RSS Readers]
end
Reader -->|Reads posts, searches content| Blog
Author -->|Writes markdown posts| GitHub
SearchEngine -->|Crawls for indexing| Blog
Blog -->|Deployed via CI/CD| GitHubPages
Blog -->|Comments integration| Giscus
Blog -->|Font loading| GoogleFonts
Blog -->|RSS feed| RSS
GitHub -->|Source code & content| Blog
Why Static Site Generation?
The biggest decision was going with Static Site Generation (SSG) instead of a traditional server or single-page app.
Why SSG?
Performance Benefits:
- Pre-built HTML files serve instantly from CDN (content delivery network - basically copies of your site stored worldwide for faster access)
- No server processing time (pages donāt need to be generated on-demand)
- Minimal JavaScript payload (less code to download)
- Excellent Core Web Vitals scores (Googleās performance metrics)
Security Advantages:
- No server-side attack surface
- No database vulnerabilities
- Content is immutable once deployed
Cost Efficiency:
- Free hosting on GitHub Pages
- No server maintenance
- Scales automatically with CDN (content gets served from servers close to your users)
Trade-offs:
- Build time increases with content volume
- Dynamic features require client-side implementation
- Content updates need full rebuilds
Technology Stack Architecture
graph TB
subgraph "Frontend Technologies"
HTML5[HTML5]
CSS3[Vanilla CSS]
VanillaJS[Vanilla JavaScript]
Shiki[Shiki<br/>Syntax Highlighting]
Lunr[Lunr.js<br/>Search Engine]
end
subgraph "Framework & Build"
Astro[Astro SSG<br/>v5.14.3]
Node[Node.js]
NPM[NPM Package Manager]
end
subgraph "Content & Data"
Markdown[Markdown Files]
YAML[YAML Frontmatter]
JSON[JSON Data Files]
end
subgraph "External Services"
GitHubPages[GitHub Pages<br/>Hosting]
GitHubActions[GitHub Actions<br/>CI/CD]
Giscus[Giscus<br/>Comments]
GoogleFonts[Google Fonts<br/>Typography]
end
Astro --> HTML5
Astro --> CSS3
Astro --> VanillaJS
VanillaJS --> Shiki
VanillaJS --> Lunr
Markdown --> Astro
YAML --> Astro
JSON --> Lunr
Node --> Astro
NPM --> Node
GitHubActions --> GitHubPages
Why Astro?
I actually discovered Astro when I saw another blog mention it was āBuilt with Astroā in the footer. The site was fast and clean, so I decided to check it out. After looking at a bunch of options, I settled on Astro:
What I liked about Astro:
- Zero JavaScript by default, pages are just HTML unless you add JS
- You can use any framework you want (React, Vue, Svelte) or none at all
- Fast builds and great performance
- Simple to get started, no complex configuration needed
vs Next.js:
- Way simpler for static content (no getStaticProps/getStaticPaths boilerplate for every page)
- Better performance for blogs (Next.js ships React runtime even for static content)
- Less configuration overhead (Next.js has tons of options for rendering modes, caching, etc.)
vs Gatsby:
- Builds are actually fast (Gatsbyās builds can take forever with lots of content)
- No GraphQL layer to learn (Gatsby forces you to query everything through GraphQL, even simple data)
- Simpler mental model (no complex plugin ecosystem to navigate)
vs Hugo:
- I know Go much better than JavaScript, but for front-end templating Iām more familiar with JavaScript patterns than Go templates (even though Iād probably pick up Hugoās templating quickly)
- Astroās component syntax feels more familiar coming from a programming background
- Better integration with modern JavaScript libraries and tools
Trade-offs:
- Smaller ecosystem than Next.js
- Less mature than Hugo
- Learning curve for Astro-specific concepts (frontmatter separation, file-based routing, static path generation)
Component Architecture
graph TB
subgraph "Web Application Components"
subgraph "Pages"
IndexPage[Index Page<br/>Homepage with post list]
BlogPost[Blog Post Page<br/>Individual post view]
SearchPage[Search Page<br/>Full-text search]
TagPages[Tag Pages<br/>Posts by tag]
DatePages[Date Pages<br/>Posts by date]
end
subgraph "Layouts"
BlogLayout[BlogPost Layout<br/>Post template]
end
subgraph "Components"
Header[Header Component<br/>Navigation & search]
Sidebar[Sidebar Component<br/>Calendar & tags]
Search[Search Component<br/>Lunr.js integration]
Comments[Comments Component<br/>Giscus integration]
end
subgraph "Utilities"
ReadingTime[Reading Time Calculator]
SearchIndexGen[Search Index Generator]
end
end
IndexPage --> Header
IndexPage --> Sidebar
BlogPost --> BlogLayout
BlogLayout --> Header
BlogLayout --> Sidebar
BlogLayout --> Comments
Header --> Search
SearchPage --> Search
Search --> SearchIndexGen
BlogLayout --> ReadingTime
How I Organized Things
Reusable Stuff:
- Header and Sidebar everywhere for consistency
- Search functionality shared across pages
- One BlogPost layout for all
Utilities:
- Reading time calculator (I really appreciate when this is at the top of any post I read)
- Search index generator (learned something by developing the blog, I had no idea you can do search without something like Elasticsearch⦠back-end dev problems)
- Keeping shared logic in one place
Data Flow Architecture
flowchart TD
subgraph "Content Creation"
A[Author writes Markdown] --> B[Git commit & push]
B --> C[GitHub Repository]
end
subgraph "Build Process"
C --> D[GitHub Actions Trigger]
D --> E[Astro Build Process]
E --> F[Process Markdown Files]
F --> G[Generate Static HTML]
F --> H[Create Search Index]
F --> I[Generate RSS Feed]
F --> J[Create Sitemap]
end
subgraph "Deployment"
G --> K[Deploy to GitHub Pages]
H --> K
I --> K
J --> K
end
subgraph "User Interaction"
K --> L[User visits site]
L --> M[Load static content]
M --> N[Client-side search]
M --> O[Interactive calendar]
M --> P[Comments loading]
end
H --> N
P --> Q[Giscus API]
The Content Pipeline
Markdown Everything:
- All posts are just Markdown files
- YAML frontmatter for the metadata
- Everything lives in Git
Why this works:
- I can write in any editor I want
- Content isnāt locked to any platform
- Easy to backup (itās just files!)
- Code blocks and formatting just work
The build process:
- Astro processes everything at build time
- Search index gets generated automatically
- RSS feed updates itself
- Sitemap gets created for SEO
The downside:
- No real-time updates (need to rebuild)
- Canāt do fancy dynamic stuff easily (but I also donāt do fancy soā¦)
Feature Architecture Deep Dives
Search System
flowchart LR
subgraph "Search Implementation"
A[Blog Posts] --> B[Build Time Processing]
B --> C[Generate search-index.json]
C --> D[Lunr.js Index Creation]
D --> E[Client-side Search]
E --> F[Dropdown Results]
E --> G[Search Page Results]
end
subgraph "Search Components"
H[Search Component] --> I[Search Input]
H --> J[Results Dropdown]
K[Search Page] --> L[Full Results Display]
end
D --> H
D --> K
Search Decisions:
Client-side with Lunr.js:
- No server needed
- Search is instant
- Works even offline
The trade-offs:
- Users download the search index (more bandwidth)
- Wonāt scale to huge sites (but I also will not write that much)
- No search analytics
- Pretty basic ranking
Free alternatives I considered:
- Basic text search: Too clunky (just matching exact words, no ranking, poor user experience)
- Simple grep-style filtering: Works but feels broken compared to real search
Calendar System
flowchart TD
A[Blog Posts with Dates] --> B[Extract Post Dates]
B --> C[Generate Calendar Grid]
C --> D[Mark Days with Posts]
D --> E[Interactive Calendar]
E --> F[Month Navigation]
E --> G[Year Navigation]
E --> H[Click to View Posts]
H --> I[Date-based Routing]
Calendar Decisions:
Made it interactive:
- JavaScript calendar with month/year navigation
- Allows month/year navigation
- Clickable dates link to posts
Implementation:
- Vanilla JavaScript (no framework dependency)
- Post dates extracted at build time, passed to client
- Fully client-side calendar generation (avoids build-time date issues)
- Calendar initializes with current date when page loads
Trade-offs:
- More complex than static calendar
- Requires JavaScript enabled
- Additional client-side code
- Slight delay on initial load while calendar generates
Comments System
flowchart TD
A[Blog Post Page] --> B[Comments Component]
B --> C[Giscus Integration]
C --> D[GitHub Discussions API]
D --> E[Load Comments]
E --> F[Display Comments]
F --> G[User Interaction]
G --> H[GitHub OAuth]
H --> I[Post Comments]
Comments Decisions:
Went with Giscus:
- Uses GitHub Discussions (free!)
- No extra accounts needed
- Taps into the GitHub community
- Good moderation built-in
vs Disqus:
- No privacy concerns or ads
- Cleaner integration
vs Self-hosted:
- Way less maintenance headache
The downside:
- Only GitHub users can comment
- Dependent on GitHub
- Limited customization
Analytics
The blog uses GoatCounter for privacy-friendly analytics. It tracks page views without cookies, collects no personal data, and is GDPR-compliant out of the box. A single script tag is all it takes to set up, and the dashboard shows which posts are being read without any of the surveillance overhead of Google Analytics.
Deployment Pipeline
flowchart TD
A[Developer writes content] --> B[Git commit to main branch]
B --> C[GitHub webhook triggers]
C --> D[GitHub Actions workflow starts]
subgraph Build["Build Process"]
D --> E[Checkout code]
E --> F[Setup Node.js]
F --> G[Install dependencies]
G --> H[Run astro build]
H --> I[Generate static files]
end
subgraph Deploy["Deployment"]
I --> J[Deploy to GitHub Pages]
J --> K[Update DNS]
K --> L[Site live]
end
subgraph Verify["Verification"]
L --> M[Health checks]
M --> N[RSS feed updated]
M --> O[Search index updated]
M --> P[Sitemap updated]
end
Deployment Setup
GitHub Actions because:
- Works perfectly with GitHub Pages
- Free for public repos
- Simple YAML config
How it works:
- Push to main ā automatic deployment
- No manual steps needed
- Fast feedback when I mess something up
Nice things:
- Zero-downtime deployments
- Rolls back if build fails
- Everything is version controlled
Performance & Security Architecture
graph TB
subgraph Security["Security Measures"]
A[Static Site Generation<br/>No server vulnerabilities]
B[GitHub Pages HTTPS<br/>SSL/TLS encryption]
C[Content Security Policy<br/>XSS protection]
D[No sensitive data<br/>Client-side only]
end
subgraph Performance["Performance Optimizations"]
E[Pre-built static files<br/>Fast loading]
F[CDN delivery<br/>GitHub Pages CDN]
G[Optimized images<br/>Proper sizing]
H[Minimal JavaScript<br/>Vanilla JS only]
I[Font optimization<br/>Google Fonts preload]
end
subgraph Monitoring["Monitoring"]
J[GoatCounter analytics]
K[RSS feed validation]
L[Search functionality testing]
end
Performance Stuff
Keep JavaScript minimal:
- Only load whatās actually needed
- Vanilla JS for simple things
- Make it work without JS first
Images:
- Proper sizing (no giant images)
- Lazy loading
- WebP when possible
Fonts:
- Google Fonts with preload
- Font-display: swap for faster loading
- Not too many font variations
Security
Static sites are pretty secure:
- No server code to exploit
- No database to hack
- Each deploy is immutable
Content Security Policy:
- Restrict where scripts can come from
- Prevent XSS
- Control external resources
What I Learned
Whatās Working Great
- Astroās performance - Zero JS by default is amazing
- Markdown workflow - Writing is smooth and content is portable
- GitHub integration - Write ā push ā deployed, no fuss
- Component reuse - Easy to maintain and update
What Could Be Better
My knowledge of front-end- Search limitations - Client-side search wonāt scale forever
- Build times - Will get slower as I write more
- Dynamic stuff - Limited options for real-time features
- Analytics - Pretty basic compared to server-side options
Future Plans
Might upgrade to:
- Astroās newer features (view transitions, content collections)
- Server-side search if this gets too slow
- Better analytics setup
Keeping an eye on:
- Build times as content grows
- Whether pagination needs improvement (currently shows 8 posts per page)
- CDN performance
Wrapping Up
This whole setup prioritizes simplicity and performance over fancy features. Astro gives me great performance and a nice developer experience, while keeping everything maintainable. Plus, since I already keep my notes in Obsidian as markdown files, the workflow feels natural, I can draft posts there and just copy them over.
The main thing I learned is that your architecture should match your actual needs. For a personal blog where I want to focus on writing and performance, this works really well. If I needed real-time features or complex user interactions, Iād probably make different choices.
The whole system is simple enough that I can actually understand it (as a backend dev whoād rather avoid frontend whenever possible), deploy with a git push, and it scales automatically without me having to babysit servers.
Comments