← All writing

This is why Astro.js should be your preferred tech stack

Abhay Bagda 22 April 2026 6 min Technical

Every modern framework has the same dirty secret. It ships JavaScript to render pages that are mostly text. Your blog post is 2000 words of prose and one image. Next.js will still hand the user a React runtime to display it.

I have built sites in Next.js. For a while I thought that was just how the web worked now. Every new project, npx create-next-app, add the router, ship the whole runtime, pretend the bundle size is someone else’s problem. If the thing I am building is a site, not an app, I open Astro.js. Every time.

If 90% of your page is static content, shipping a hydrated React tree to render it is a tax you are paying on behalf of every visitor who did not need it.

What hydration actually costs you

Most frameworks render your page twice. Once on the server to produce HTML, once in the browser to wake the page up. That second pass is called hydration. It runs after the HTML arrives, re-executes your entire component tree in the browser, and attaches event listeners so the page becomes interactive. It is the reason your “fast” Next.js site still feels sluggish on a mid-range Android.

Hydration is not free. The user pays for it with JavaScript downloads, parse time, and execution delay. If your page is a blog post or a landing page with nothing to click beyond a link, they are paying for nothing.

Shout out to my friend Nigel, who spent enough conversations on efficiency and optimisation for the idea to finally stick. I never treated it as a real problem before. Singapore has some of the fastest consumer internet on the planet and I was testing every site on a Google Pixel 10 Pro. Load times were always fine, so I assumed they were fine for everyone. They are not. Most of the world is on a mid-range Android, on a patchy connection, waiting for your bundle to parse. That is where the UX quietly falls off a cliff, and it is the part you never see from your own laptop.

Astro.js flips the default. Every .astro component renders to HTML at build time and ships zero JavaScript. If one corner of the page actually needs interactivity, a search bar, a newsletter form, a dropdown, you mark that one component and only that one hydrates. The rest stays as plain HTML. You end up shipping 10 KB of JS where Next.js would ship 200.

SEO is not a plugin, it is a consequence

The thing that decides whether people find your site is not your design. It is how fast Google can crawl it, render it, and rank it. Static HTML with real content in the initial response is the single best signal you can give a search engine. Astro.js ships that by default.

Your content lives in the initial HTML. Not fetched after hydration, not injected by a client-side useEffect, not hidden behind a loading state. Googlebot asks for the page, gets the text, moves on. Next.js can do this too with enough ceremony. Astro does it because it is the only thing it knows how to do.

Core Web Vitals are part of how Google ranks you now. Largest Contentful Paint, Cumulative Layout Shift, Interaction to Next Paint. Static HTML wins all three without you writing a line of performance code. I have shipped Astro.js sites that hit 99 on Lighthouse without trying.

Your meta tags do not drift. Server-rendered pages with typed frontmatter make it hard to forget a title or an Open Graph image. The schema catches it at build, before it ever reaches a search engine.

For a website whose entire job is to rank, this is the difference between “we are working on SEO” and “we do not need to.”

The one feature that saved me the most time

The site you are reading now runs on Astro.js. Every article is a markdown file in a folder. That sounds boring until you see what happens underneath.

// src/content.config.ts
import { defineCollection, z } from 'astro:content'

const writing = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    tag: z.enum(['Technical', 'Quick Take', 'Tools']),
    date: z.date(),
    excerpt: z.string(),
    cover: z.string(),
  }),
})

export const collections = { writing }

This is called a Content Collection. It treats my .mdx files like a local database. The schema is validated with Zod, which means every frontmatter field is typed. If I forget a date, the build fails with a readable error pointing at the offending file. If I put the wrong tag, it tells me the allowed values. I do not find out about a broken frontmatter field in production at 11pm on a Friday. I find out on the next npm run dev with a message that tells me exactly what is wrong.

The rest of the site reads from that collection. The homepage queries it for the latest three posts. The writing index queries it sorted by date. Each article page queries it by slug. Same typed function, same validated data, every time.

I type my articles in markdown. The build validates them. The site renders them. No CMS to babysit, no database to migrate, no runtime API to secure. Git is the CMS. That is the whole stack.

Where Astro.js stops being the answer

Astro.js is not the right tool for everything. I am not going to pretend it is.

It wins when the site is mostly content. Marketing pages, blogs, documentation, e-commerce catalogues, portfolios. Anywhere the job is to render information clearly and get out of the way. These are the sites Google ranks. These are the sites that need to load in under a second on a Jakarta 4G connection. This is where Astro.js is unbeatable.

It is the wrong tool for complex SaaS dashboards, auth-heavy web apps, and anything where the UI itself is the product. If 80% of your page is interactive state, a framework that hydrates by default is the right choice. That is what Next.js exists for. Using Astro.js there is fighting the tool.

The trick is knowing which one you are building. If your site is a brochure with a contact form, it is a site. If your site is Figma, it is an app. Pick accordingly.

Stop reaching for Next.js by default

Most of what people put on the internet is content. Articles, landing pages, product information, docs. None of that needs a JavaScript runtime to render. If you are opening create-next-app for a marketing site because that is the tutorial you found, you are paying the user to download a framework they never needed.

Open Astro.js. Define a collection. Write your pages as markdown. Hydrate the one component that actually moves. Watch Google start treating you better and your Lighthouse score climb for free.

If you want the full breakdown from the Astro.js team, read the docs here. Fair warning, once it clicks, you will stop reaching for anything else for a site.