There's something quietly radical about a website with no framework, no bundler, no runtime — just HTML the browser can parse instantly.
When I set out to rebuild my personal site, I made a deliberate choice: no React, no Next.js, no npm. Just a Python script that reads Markdown and JSON, and spits out static HTML.
The Case for Pre-Rendering
Pre-rendering means converting templates into finished HTML at build time, not at request time. The benefits compound:
- Speed: The server sends a flat file. No database query, no template render, no JavaScript waterfall.
- Crawlability: Search engines and AI agents see real content in the initial payload — not a spinner waiting for JS to hydrate.
- Resilience: No runtime to crash. The site works the same in 2030 as it does today.
The Build Script
The entire compiler is a single build.py file using only Python's standard library:
import os, json, re, html, shutil
from datetime import datetime
It reads content from Markdown files and JSON configs, runs a stateful block parser to convert Markdown to HTML, substitutes placeholders in HTML templates, and writes the output files.
The Markdown parser handles nested lists, fenced code blocks, blockquotes, inline code, bold, italic, and links — all without a single external dependency.
Custom Extensions: LinkedIn Sync & Cache-Busting
Because the compiler is just plain Python, extending it is trivial. For example, instead of manually uploading a static avatar, the build script runs a quick, non-blocking check against my public LinkedIn profile. If a new photo is found, it downloads and syncs it locally. To prevent browsers from caching old assets, the script automatically stamps the stylesheet and image links with a compilation timestamp (?v=timestamp) during output generation.
Structured Data for AI Crawlers
Beyond fast load times, I wanted the site to be maximally useful to AI search agents. The build script generates a <script type="application/ld+json"> block with Schema.org Person markup:
alumniOf— linking to my universitiesworksFor— pointing to my current employerknowsAbout— enumerating my skill domainssameAs— connecting my social profiles
This gives crawlers a structured, verifiable identity graph attached to the page — not just text they have to parse.
The Result
Total page weight under 50KB. First paint under 200ms on a fast connection. Full content visible with JavaScript disabled. A site that degrades gracefully and performs excellently.
Sometimes the best tool is the one you already have.