Rebuilding malkiatms.in with Astro and Vercel
Why I replaced my old template site with a fast, Markdown-first blog built on Astro, and how it runs on the Vercel free plan.
On this page
For a long time my personal site was a purchased HTML template with my name dropped into it. It had a video popup, a portfolio grid full of placeholder projects, and testimonials from people I had never met. It said very little about the work I actually do, and I never wrote anything on it.
So I rebuilt it. This post covers what I wanted from the new site, the stack I picked, and how publishing works now.
What I wanted
My list was short:
- Writing first. A blog I will actually use, where a new post is a single Markdown file.
- Fast by default. No client-side framework unless a page needs it.
- Free to host. No server to patch and no monthly bill for a personal site.
- Easy to change. Clear structure and docs, so future me (or an AI coding agent) can make changes without re-learning everything.
The stack
- Astro builds the site. Pages are
.astrocomponents that render to plain HTML at build time, and blog posts are Markdown files in a typed content collection. - Vercel hosts it. Every push to
maintriggers a build and deploy, and pull requests get their own preview URLs. - Shiki handles syntax highlighting at build time, so code blocks cost zero JavaScript in the browser.
The result is a static site: HTML, CSS and a few tiny scripts for the theme toggle and the copy buttons on code blocks.
Writing a post
A post is a Markdown file in src/content/blog/ with some frontmatter:
---
title: 'Rebuilding malkiatms.in with Astro and Vercel'
description: 'Why I replaced my old template site...'
pubDate: 2026-09-29
tags: ['astro', 'vercel']
draft: false
---
Post content goes here.
Astro validates the frontmatter against a schema, so a missing title or a malformed date fails the build instead of shipping a broken page. Drafts show up locally in npm run dev and are left out of production builds.
The URL comes from the file name, so rebuilding-malkiatms-in.md becomes /blog/rebuilding-malkiatms-in/.
Content collections in practice
The schema lives in src/content.config.ts:
const blog = defineCollection({
loader: glob({ base: './src/content/blog', pattern: '**/[^_]*.{md,mdx}' }),
schema: ({ image }) =>
z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
heroImage: image().optional(),
}),
});
Pages then query posts with full type safety. The blog index, tag pages, RSS feed and sitemap are all generated from this one collection.
Deploying on the free plan
Vercel detects Astro automatically, so there is no adapter to install for a static site. The workflow is:
git add .
git commit -m "New post: rebuilding my site"
git push
About a minute later the change is live. Custom domains and HTTPS are included on the free Hobby plan.
What’s next
I plan to write about the things I work on every day: Angular and Ionic apps, Node.js and .NET APIs, working with databases, and adding AI features to real products. If that sounds useful, subscribe via RSS or connect with me on LinkedIn.