I rebuilt this portfolio site with Astro, and honestly, it’s the best decision I made for a content-heavy site. Here’s why.
The JavaScript Problem
Most modern frameworks ship tons of JavaScript to the browser. React, Vue, Next.js—they all hydrate the entire page even if you just have a static blog post. Your readers are downloading and executing JavaScript just to read text. That’s ridiculous.
Astro ships zero JavaScript by default. Your blog post is just HTML and CSS. Fast, simple, perfect.
But What About Interactivity?
Here’s where Astro gets clever. You can still use React, Vue, Svelte—whatever you want. But you choose what gets JavaScript.
See that floating action button on this site? That’s React with client:load. The rest of the page? Pure HTML.
---
import FloatingButton from '@/components/FloatingButton';
---
<FloatingButton client:load />
This is called “Islands Architecture.” Interactive components are islands in a sea of static HTML.
Content Collections Are Game-Changing
Before Astro, my blog posts were just files. No validation, no type safety, no structure.
Content Collections changed that:
const blog = defineCollection({
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
heroImage: z.string().optional(),
}),
});
Now TypeScript knows the exact shape of every blog post. Forget to add a title? Build fails. Try to use the wrong type? Your editor yells at you.
MDX: Markdown + Components
I can write normal Markdown:
## Regular Markdown
Just writing content...
But when I need something interactive:
## Interactive Section
<InteractiveDemo />
<CodePlayground code={exampleCode} />
Components right inside Markdown. No weird workarounds.
Performance Is Insane
My Lighthouse scores:
- Performance: 100
- Accessibility: 100
- Best Practices: 100
- SEO: 100
That’s not because I’m amazing at optimization. That’s just Astro being smart about what it ships.
The Developer Experience
Astro files look like this:
---
// JavaScript goes here
const posts = await getCollection('blog');
---
<!-- HTML goes here -->
<div>
{posts.map(post => (
<PostCard title={post.data.title} />
))}
</div>
Frontmatter for logic, HTML below. Clean and intuitive.
When NOT to Use Astro
Astro isn’t for everything:
- Dashboards with tons of interactivity? Use Next.js or Remix
- Real-time apps? Astro isn’t built for that
- Complex client-side routing? You’ll have a bad time
Astro is perfect for:
- Blogs (obviously)
- Marketing sites
- Documentation
- Portfolios (like this one)
- Any content-first website
Mixing Frameworks
This is wild—you can use React AND Vue in the same project:
---
import ReactComponent from './Component.jsx';
import VueComponent from './Component.vue';
---
<ReactComponent client:load />
<VueComponent client:visible />
I don’t recommend it, but you can do it.
The Actual Getting Started
npm create astro@latest
Pick a template, install dependencies, done. The CLI is genuinely good—it asks the right questions and sets everything up correctly.
For this portfolio, I used:
- Astro for pages and layouts
- React for interactive UI (with
client:load) - Tailwind for styling
- MDX for blog posts with custom components
Real Talk
Astro isn’t perfect. The ecosystem is smaller than Next.js. Some integrations are rough around the edges. But for content-heavy sites where performance matters, nothing comes close.
If you’re building a blog, portfolio, or marketing site, try Astro. You might not go back.