My portfolio started life as a Gatsby site built from a template. It served me
well for years, but every time I came back to it the gap between what the site
needed and what the stack demanded had widened: a React runtime shipped to
render what is essentially a text document, a GraphQL layer between me and my
own markdown, and a plugin ecosystem that seemed to need care and feeding every
time I ran npm install.
Rebuilding on Astro taught me a few things I wish I had known earlier.
The site is a document, not an app
The single biggest mental shift was admitting that my portfolio has almost no interactivity. A contact form, a scroll-reveal animation — that is the whole JavaScript budget. Astro’s model of shipping zero JavaScript by default, and opting in per component only where interactivity lives, matches that reality exactly. Gatsby’s model — hydrate a React tree everywhere and work backwards — never did.
The numbers make the point better than the argument does. The rebuilt site ships HTML, CSS, a font subset, and one small script for the reveal animations. There is no framework runtime at all.
You do not need a data layer for six markdown files
Gatsby routes everything through GraphQL, which is impressive engineering and complete overkill for a personal site. Astro replaces it with typed data: plain TypeScript objects for structured content, and content collections with a zod schema for markdown. If I typo a date in frontmatter, the build fails with an error that points at the exact file and field. That is the entire feature, and it is the right size.
const writing = defineCollection({
loader: glob({ pattern: "**/[^_]*.md", base: "./src/content/writing" }),
schema: z.object({
title: z.string().max(80),
publishedDate: z.coerce.date(),
draft: z.boolean().default(false),
}),
});
Templates age faster than sites
The old site was a customised template, and the template’s decisions outlived
their welcome: gradient buttons, clip-path section dividers, centred text
everywhere. Rebuilding forced me to make my own decisions — a token system with
CSS custom properties, a real dark scheme driven by prefers-color-scheme,
and typography chosen on purpose rather than inherited.
The lesson is not “never use a template”. It is that a template is a loan, and you repay it the first time you want the site to say something specific about you.
Small traps I hit
- Node versions matter. Astro pins a minimum Node version and will refuse
to start on anything older. Worth encoding in
enginesso the failure is loud and early. - Image optimisation is built in, but only if you use it.
astro:assetswants images imported fromsrc/, not referenced frompublic/. The difference is responsivesrcsetand modern formats for free. - Forced dark mode is real. If a site does not declare
color-scheme, Chrome may invert it — and a design that was never tested dark can end up with black text on black inputs. Declaring the scheme and designing both modes fixed a bug I did not know I had.
Was it worth it?
Yes — but not mainly for the performance, which was the headline reason I started. The real value was that the rebuild made the site cheap to change. Adding this writing section was a schema, two pages, and an RSS endpoint, because the foundations are plain markdown, typed data, and HTML. That is the property I want in every codebase I touch, and it took rebuilding my own site to see it clearly.