How this site is built
Vinext on Cloudflare Workers, Markdown compiled at build time, a D1 database for the interactive bits, and very little JavaScript in the browser.
This site is small, but I wanted it to be fast, cheap to run, and fully mine: the posts, the comments and the newsletter list all live in places I control.
The stack
| Piece | Choice |
|---|---|
| Framework | vinext, the Next.js API on Vite |
| Runtime | Cloudflare Workers, via OpenAI Sites |
| Database | Cloudflare D1 (SQLite) with Drizzle ORM |
| Styling | Tailwind CSS v4 |
| Content | Markdown files in the repo |
Markdown, compiled ahead of time
Posts are plain .md files with front matter. A small Vite plugin turns each file into a JavaScript module during the build, so the server never parses Markdown or highlights code when you load a page:
export function markdownContent(): Plugin {
return {
name: "sabinolab:markdown-content",
enforce: "pre",
async transform(code, id) {
if (!id.endsWith(".md")) return null;
const doc = await renderMarkdown(code, id, contentDir);
return { code: `export default ${JSON.stringify(doc)};`, map: null };
},
};
}
That module includes the rendered HTML, a table of contents, the reading time, and plain text for search.
Syntax highlighting
Code blocks are highlighted by Shiki at build time too, so there's no highlighting library in the browser.
The interactive parts
Comments, the contact form and the newsletter all post plain HTML forms to route handlers. They work without JavaScript, and everything is stored in D1:
CREATE TABLE subscribers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL UNIQUE,
status TEXT NOT NULL DEFAULT 'pending',
token TEXT NOT NULL UNIQUE
);
Owning the subscriber list means I can export it as CSV at any time and switch email providers without asking anyone.
Projects from GitHub
The projects page asks the GitHub API for my public repositories, caches the answer in D1 for an hour, and serves the last good copy if GitHub is unreachable. A small overrides file lets me feature a project, rename it or add screenshots without touching code.
Comments
No comments yet. Start the conversation!