Building Lightning-Fast Sites with Astro and TypeScript
- Mokshit Jain
- Frontend Development , Web Development
- 25 Jan, 2026
After years of using React, Next.js, and Vue for every project, I discovered Astro—and it completely changed my perspective on when to use JavaScript frameworks. For content-focused websites, portfolios, and documentation sites, Astro delivers unmatched performance and developer experience.
Why Astro?
Astro takes a different approach: islands architecture. Instead of hydrating your entire page with JavaScript, it ships zero JavaScript by default and only adds interactivity where you explicitly need it.
The Performance Difference
| Metric | Traditional SPA | Astro |
|---|---|---|
| Initial JS Bundle | ~200-500 KB | ~0 KB |
| Time to Interactive | 2-4 seconds | < 1 second |
| Lighthouse Score | 60-80 | 95-100 |
| SEO | Requires SSR | Built-in perfect |
Getting Started with Astro
Project Setup
npm create astro@latest my-portfolio
cd my-portfolio
npx astro add tailwind # Add Tailwind CSS
npx astro add react # Add React for islands
Basic Page Structure
---
// src/pages/index.astro
import Layout from "../layouts/Layout.astro";
import Card from "../components/Card.astro";
const pageTitle = "My Portfolio";
const projects = [
{ title: "Project A", description: "..." },
{ title: "Project B", description: "..." },
];
---
<Layout title={pageTitle}>
<main>
<h1>{pageTitle}</h1>
<div class="grid">
{projects.map((project) => <Card {project} />)}
</div>
</main>
</Layout>
The code between --- is server-side—it doesn’t ship to the browser!
TypeScript Integration
Astro has first-class TypeScript support. Enable it in your config:
// astro.config.mjs
import { defineConfig } from "astro/config";
export default defineConfig({
typescript: {
strict: true,
strictNullChecks: true,
},
});
Type-Safe Components
---
// src/components/ProjectCard.astro
interface Project {
title: string;
description: string;
tags: string[];
image: string;
link: string;
}
interface Props {
project: Project;
}
const { project } = Astro.props;
---
<div class="card">
<img src={project.image} alt={project.title} />
<h3>{project.title}</h3>
<p>{project.description}</p>
<div class="tags">
{project.tags.map((tag) => <span class="tag">{tag}</span>)}
</div>
<a href={project.link}>View Project</a>
</div>
<style>
.card {
/* Scoped CSS - no global pollution! */
border-radius: 8px;
padding: 1.5rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
</style>
Content Collections
For blogs, docs, or any structured content, Astro’s Content Collections are incredible:
// src/content.config.ts
import { defineCollection, z } from "astro:content";
const blog = defineCollection({
type: "content",
schema: z.object({
title: z.string(),
description: z.string(),
date: z.date(),
tags: z.array(z.string()),
draft: z.boolean().default(false),
}),
});
export const collections = { blog };
Now create markdown files with frontmatter:
---
title: "My First Post"
description: "Learning Astro"
date: 2025-02-15
tags: ["astro", "webdev"]
draft: false
---
# Content goes here...
Querying Collections in TypeScript
---
import { getCollection } from "astro:content";
const allPosts = await getCollection("blog");
const publishedPosts = allPosts
.filter((post) => !post.data.draft)
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
---
{
publishedPosts.map((post) => (
<article>
<h2>{post.data.title}</h2>
<p>{post.data.description}</p>
<a href={`/blog/${post.slug}`}>Read more</a>
</article>
))
}
Islands Architecture: Adding Interactivity
When you need JavaScript, use React components as “islands”:
---
import InteractiveMap from "../components/InteractiveMap.jsx";
---
<!-- Static content (no JS) -->
<div>
<h1>Contact</h1>
<p>Find us on the map:</p>
<!-- Interactive island (React component) -->
<InteractiveMap client:load />
</div>
Client Directives
client:load- Hydrate immediately on page loadclient:idle- Hydrate when browser is idleclient:visible- Hydrate when element enters viewportclient:media="{query}"- Hydrate when media query matches
Image Optimization
Astro’s built-in image optimization is fantastic:
---
import { Image } from "astro:assets";
import myHeroImage from "../images/hero.jpg";
---
<Image
src={myHeroImage}
alt="Hero section"
widths={[400, 800, 1200]}
formats={["webp", "jpeg"]}
loading="eager"
/>
This automatically:
- Generates multiple sizes
- Converts to modern formats (WebP)
- Lazy loads by default
- Serves optimal format per browser
Building a Portfolio: Practical Example
Here’s a pattern I use for portfolio projects:
---
// src/pages/projects/[slug].astro
import { getCollection } from "astro:content";
import ProjectHeader from "../../components/ProjectHeader.astro";
import ProjectGallery from "../../components/ProjectGallery.jsx";
export async function getStaticPaths() {
const projects = await getCollection("projects");
return projects.map((project) => ({
params: { slug: project.slug },
props: { project },
}));
}
const { project } = Astro.props;
const { Content } = await project.render();
---
<Layout title={project.data.title}>
<article>
<ProjectHeader project={project} />
<Content />
<ProjectGallery client:visible images={project.data.images} />
</article>
</Layout>
Performance Tips
1. Minimize JavaScript
# Check your bundle size
npx astro build
npx astro preview
2. Use Preloading
---
<link
rel="preload"
href="/fonts/main.woff2"
as="font"
type="font/woff2"
crossorigin
/>;
---
3. Optimize Images
<Image
src={image}
formats={["avif", "webp"]}
Modern
formats
first
loading="lazy"
Lazy
load
below
fold
decoding="async"
Decode
asynchronously
/>
4. Inline Critical CSS
// astro.config.mjs
export default defineConfig({
build: {
inlineStylesheets: "auto",
},
});
Deployment
Astro builds to static files—deploy anywhere:
# Build for production
npm run build
# Output in /dist - ready to deploy
Deploy to:
- Vercel:
vercel deploy - Netlify:
netlify deploy --prod - GitHub Pages: Use GitHub Actions
- Cloudflare Pages: Direct connect
When to Use Astro
Perfect for:
- ✅ Portfolios and resumes
- ✅ Blogs and documentation
- ✅ Marketing sites
- ✅ E-commerce product pages
- ✅ Any content-focused site
Not ideal for:
- ❌ Complex dashboards
- ❌ Real-time applications
- ❌ Heavy client-side state
Conclusion
Astro delivers the best of both worlds: the simplicity of static sites with the flexibility of component frameworks. For my portfolio and content sites, it’s been a game-changer—lightning-fast load times, excellent SEO, and a delightful developer experience.
If you’re building a content-focused website, give Astro a try. You might never go back to traditional SPAs for this use case.
Have questions about Astro or want to share your experience? Let’s connect!