Learn Astro.js - Complete Beginner's Guide
Master Astro.js from the ground up. Learn why Astro is the fastest framework for content-driven websites and how to build lightning-fast web applications.
π Learn Astro.js - The Complete Guide
Welcome to the complete guide to Astro.js! Whether youβre a seasoned developer or just starting your web development journey, this guide will teach you everything you need to know to build blazing-fast websites with Astro.
π Table of Contents
- What is Astro?
- Why Choose Astro?
- Prerequisites
- Getting Started
- Project Structure
- Core Concepts
- Working with Components
- Pages and Routing
- Layouts
- Data Fetching
- Content Collections
- Islands Architecture
- Integrations
- Styling
- Deployment
- Best Practices
π What is Astro?
Astro is a modern web framework for building fast, content-focused websites. Itβs designed from the ground up to deliver better performance by shipping less JavaScript to the browser.
Key Philosophy
Astro follows a simple but powerful philosophy:
- Content-first: Built specifically for content-heavy sites (blogs, marketing sites, documentation, e-commerce)
- Server-first: HTML is rendered on the server, not in the browser
- Zero JS by default: Only ship JavaScript when you actually need it
- Framework agnostic: Use React, Vue, Svelte, or any other frameworkβall in the same project
What Makes Astro Different?
Unlike traditional JavaScript frameworks (React, Vue, Next.js), Astro:
- Ships zero JavaScript by default - Your site loads instantly
- Allows framework mixing - Use React for one component, Vue for another
- Generates static HTML - Pre-renders pages at build time
- Enables partial hydration - Only interactive components load JavaScript (Islands Architecture)
Think of Astro as the βbest of all worldsβ: the performance of static sites + the flexibility of dynamic frameworks.
β‘ Why Choose Astro?
Performance Benefits
Astro sites are incredibly fast because:
- Zero JavaScript overhead: Most sites ship 40-90% less JavaScript
- Automatic optimization: Images, fonts, and CSS are optimized out of the box
- Partial hydration: Interactive components load independently
- Edge-ready: Deploy to edge networks for sub-100ms response times
Real numbers:
- Astro sites typically score 100/100 on Lighthouse
- 2-3x faster load times compared to traditional frameworks
- 10x less JavaScript shipped to browsers
Developer Experience
Why developers love Astro:
β Familiar syntax: If you know HTML, CSS, and JavaScript, you know Astro β Use any framework: React, Vue, Svelte, Solidβor none at all β TypeScript support: Built-in TypeScript without configuration β Markdown & MDX: Write content in Markdown with component support β Built-in features: Image optimization, RSS feeds, sitemaps included β Great DX: Hot module replacement, helpful error messages, excellent docs
Perfect Use Cases
Astro excels at:
- π Blogs and content sites - Fast page loads, SEO-friendly
- ποΈ E-commerce storefronts - Quick product pages, better conversions
- π Documentation sites - Clean, searchable, accessible
- π± Marketing websites - High performance = better SEO = more leads
- π¨ Portfolio sites - Showcase work without bloated JavaScript
Not ideal for:
- Highly interactive SPAs (Single Page Applications)
- Real-time dashboards with constant data updates
- Apps requiring persistent client-side state across many pages
π§° Prerequisites
Before starting with Astro, you should have:
Required Knowledge
- HTML & CSS basics: Understanding of web fundamentals
- JavaScript fundamentals: Variables, functions, arrays, objects
- Terminal/Command line: Basic navigation and commands
- Package managers: Familiarity with npm or pnpm
Required Software
- Node.js: Version 18.14.1 or higher (Download here)
- Package manager: npm (included with Node.js), pnpm, or yarn
- Code editor: Visual Studio Code recommended
- Git (optional but recommended): For version control
Recommended Tools
- Astro VS Code Extension: Syntax highlighting and IntelliSense
- Browser DevTools: Chrome or Firefox developer tools
- Terminal: iTerm2 (Mac), Windows Terminal, or integrated terminal in VS Code
Optional Knowledge (Helpful but not required)
- TypeScript basics
- React, Vue, or Svelte (if you want to use them)
- Static site generation concepts
- Git and GitHub
Donβt worry if you donβt know everything! Astro is beginner-friendly and youβll learn as you go.
π οΈ Getting Started
Installation
Create a new Astro project in seconds:
# Using npm
npm create astro@latest
# Using pnpm (faster)
pnpm create astro@latest
# Using yarn
yarn create astro
Setup Wizard
The CLI will guide you through setup:
astro Launch sequence initiated.
dir Where should we create your new project?
./my-astro-site
tmpl How would you like to start your new project?
β Use blog template
β Empty
β Include sample files
ts Do you plan to write TypeScript?
β Yes β No
use How strict should TypeScript be?
β Strict
β Strictest
β Relaxed
deps Install dependencies?
β Yes β No
git Initialize a new git repository?
β Yes β No
Pro tip: Choose βInclude sample filesβ for your first project to see examples.
Start Development Server
cd my-astro-site
npm install
npm run dev
Your site is now running at http://localhost:4321 π
Available Commands
npm run dev # Start dev server at localhost:4321
npm run build # Build production site to ./dist/
npm run preview # Preview built site locally
npm run astro -- # Run Astro CLI commands
π Project Structure
Understanding the file structure is key to working efficiently with Astro:
my-astro-site/
βββ public/ # Static assets (images, fonts, etc.)
β βββ favicon.svg
βββ src/
β βββ components/ # Reusable UI components
β β βββ Header.astro
β βββ layouts/ # Page layouts
β β βββ BaseLayout.astro
β βββ pages/ # File-based routing (becomes URLs)
β β βββ index.astro # Homepage (/)
β β βββ about.astro # About page (/about)
β β βββ blog/
β β βββ post-1.md # Blog post (/blog/post-1)
β βββ content/ # Content collections (Markdown/MDX)
β β βββ blog/
β βββ styles/ # CSS/SCSS files
β βββ global.css
βββ astro.config.mjs # Astro configuration
βββ package.json # Project dependencies
βββ tsconfig.json # TypeScript configuration
Key Directories Explained
public/
- Static files served as-is
- Not processed by Astro
- Use for images, fonts,
robots.txt, etc. - Access via
/filename.ext(e.g.,/logo.webp)
src/pages/
- File-based routing: Each file becomes a URL
- Supports
.astro,.md,.mdx,.html, and framework files index.astro= homepage (/)- Folders create URL paths
src/components/
- Reusable UI components
- Not automatically routed
- Can be
.astroor framework components (.jsx,.vue,.svelte)
src/layouts/
- Template wrappers for pages
- Define common structure (header, footer, meta tags)
- Imported and used in pages
src/content/
- Content collections (blog posts, products, docs)
- Type-safe with schema validation
- Optimized for Markdown/MDX content
π― Core Concepts
Astro Components
Astro components (.astro files) have a unique structure:
---
// Component Script (Runs at build time)
const pageTitle = "My Astro Page";
const items = ["Item 1", "Item 2", "Item 3"];
// Fetch data (runs on server)
const response = await fetch('https://api.example.com/data');
const data = await response.json();
---
<!-- Component Template (HTML) -->
<html>
<head>
<title>{pageTitle}</title>
</head>
<body>
<h1>{pageTitle}</h1>
<ul>
{items.map(item => <li>{item}</li>)}
</ul>
</body>
</html>
Key features:
- Frontmatter fence (
---): JavaScript/TypeScript that runs at build time - Template: HTML with JSX-like syntax
- No client-side JavaScript by default: Everything runs on the server
Component Props
Pass data to components like React:
---
// src/components/Greeting.astro
const { name, age } = Astro.props;
---
<div>
<h1>Hello, {name}!</h1>
<p>You are {age} years old.</p>
</div>
Use it:
---
import Greeting from '../components/Greeting.astro';
---
<Greeting name="John" age={30} />
Astro.props and TypeScript
Type-safe props with TypeScript:
---
// src/components/Card.astro
interface Props {
title: string;
description?: string;
image: string;
}
const { title, description, image } = Astro.props;
---
<div class="card">
<img src={image} alt={title} />
<h2>{title}</h2>
{description && <p>{description}</p>}
</div>
π§© Working with Components
Creating Your First Component
1. Create a Header component:
---
// src/components/Header.astro
const navItems = [
{ href: '/', label: 'Home' },
{ href: '/about', label: 'About' },
{ href: '/blog', label: 'Blog' },
];
---
<header>
<nav>
<ul>
{navItems.map(item => (
<li><a href={item.href}>{item.label}</a></li>
))}
</ul>
</nav>
</header>
<style>
header {
background: #1e293b;
padding: 1rem;
}
nav ul {
display: flex;
gap: 2rem;
list-style: none;
}
nav a {
color: white;
text-decoration: none;
}
</style>
2. Use it in a page:
---
// src/pages/index.astro
import Header from '../components/Header.astro';
---
<html>
<head>
<title>My Site</title>
</head>
<body>
<Header />
<main>
<h1>Welcome!</h1>
</main>
</body>
</html>
Scoped Styles
Styles in Astro components are automatically scoped to that component:
<div class="card">
<h2>Card Title</h2>
</div>
<style>
/* Only applies to THIS component */
.card {
border: 1px solid #ccc;
padding: 1rem;
}
</style>
No CSS conflicts, no naming conventions needed!
Slots
Use slots to pass content to components:
---
// src/components/Card.astro
---
<div class="card">
<slot /> <!-- Content goes here -->
</div>
<Card>
<h2>My Card Title</h2>
<p>Card content here!</p>
</Card>
Named slots:
---
// src/components/Layout.astro
---
<div>
<header>
<slot name="header" />
</header>
<main>
<slot /> <!-- Default slot -->
</main>
<footer>
<slot name="footer" />
</footer>
</div>
<Layout>
<h1 slot="header">Page Title</h1>
<p>Main content</p>
<p slot="footer">Β© 2025</p>
</Layout>
π Pages and Routing
File-Based Routing
Astro uses file-based routing - the file structure in src/pages/ determines your URLs:
src/pages/
βββ index.astro β /
βββ about.astro β /about
βββ contact.astro β /contact
βββ blog/
β βββ index.astro β /blog
β βββ post-1.astro β /blog/post-1
β βββ post-2.md β /blog/post-2
βββ products/
βββ [id].astro β /products/123 (dynamic)
Dynamic Routes
Create dynamic pages with [param] syntax:
---
// src/pages/products/[id].astro
export async function getStaticPaths() {
return [
{ params: { id: '1' } },
{ params: { id: '2' } },
{ params: { id: '3' } },
];
}
const { id } = Astro.params;
---
<h1>Product {id}</h1>
With data:
---
export async function getStaticPaths() {
const products = await fetch('https://api.example.com/products')
.then(r => r.json());
return products.map(product => ({
params: { id: product.id },
props: { product }
}));
}
const { product } = Astro.props;
---
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>${product.price}</p>
Markdown Pages
Markdown files in src/pages/ automatically become pages:
---
layout: ../../layouts/BlogLayout.astro
title: "My First Post"
author: "Ramon"
date: "2025-01-15"
---
# My First Blog Post
This is **Markdown** content that becomes an HTML page!
π¨ Layouts
Layouts wrap pages with common structure:
---
// src/layouts/BaseLayout.astro
const { title } = Astro.props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{title} - My Site</title>
</head>
<body>
<header>
<nav>
<a href="/">Home</a>
<a href="/blog">Blog</a>
</nav>
</header>
<main>
<slot /> <!-- Page content -->
</main>
<footer>
<p>Β© 2025 My Site</p>
</footer>
</body>
</html>
<style is:global>
body {
margin: 0;
font-family: system-ui;
}
main {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
</style>
Use in pages:
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="About">
<h1>About Us</h1>
<p>Welcome to our site!</p>
</BaseLayout>
π Data Fetching
Fetch at Build Time
Fetch data during the build (server-side):
---
// Runs at build time, not in the browser
const response = await fetch('https://api.github.com/users/withastro');
const user = await response.json();
---
<div>
<h1>{user.name}</h1>
<p>{user.bio}</p>
<img src={user.avatar_url} alt={user.name} />
</div>
No API keys exposed, no client-side requests!
Environment Variables
Store secrets safely:
# .env
API_KEY=your_secret_key
PUBLIC_API_URL=https://api.example.com
---
// Private (server-only)
const apiKey = import.meta.env.API_KEY;
// Public (accessible in browser)
const apiUrl = import.meta.env.PUBLIC_API_URL;
const data = await fetch(`${apiUrl}/data`, {
headers: { 'Authorization': `Bearer ${apiKey}` }
}).then(r => r.json());
---
<div>{data.message}</div>
Rule: Prefix with PUBLIC_ to expose to the browser, otherwise server-only.
π Content Collections
Content Collections are Astroβs built-in way to manage Markdown/MDX content with type safety.
Setup
1. Define your collection schema:
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const blogCollection = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
description: z.string(),
author: z.string(),
date: z.date(),
tags: z.array(z.string()),
image: z.string().optional(),
}),
});
export const collections = {
'blog': blogCollection,
};
2. Add content:
---
# src/content/blog/my-first-post.md
title: "Getting Started with Astro"
description: "Learn how to build fast websites with Astro"
author: "Ramon Nuila"
date: 2025-01-15
tags: ["astro", "web development", "javascript"]
---
# Getting Started with Astro
Content goes here...
3. Query and display:
---
// src/pages/blog/index.astro
import { getCollection } from 'astro:content';
const blogPosts = await getCollection('blog');
const sortedPosts = blogPosts.sort((a, b) =>
b.data.date.getTime() - a.data.date.getTime()
);
---
<h1>Blog Posts</h1>
{sortedPosts.map(post => (
<article>
<h2><a href={`/blog/${post.slug}`}>{post.data.title}</a></h2>
<p>{post.data.description}</p>
<small>{post.data.date.toLocaleDateString()}</small>
</article>
))}
4. Create dynamic pages:
---
// src/pages/blog/[...slug].astro
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map(post => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await post.render();
---
<article>
<h1>{post.data.title}</h1>
<p>By {post.data.author} on {post.data.date.toLocaleDateString()}</p>
<Content />
</article>
ποΈ Islands Architecture
Astroβs Islands Architecture is revolutionary: only interactive components load JavaScript.
The Problem with Traditional Frameworks
Traditional SPAs (React, Vue) ship all JavaScript to the browser, even for static content.
Astroβs solution: Ship zero JavaScript by default, hydrate only what needs interactivity.
Client Directives
Control when components load JavaScript:
---
import ReactCounter from '../components/ReactCounter.jsx';
---
<!-- Never loads JavaScript (static HTML only) -->
<ReactCounter />
<!-- Load immediately -->
<ReactCounter client:load />
<!-- Load when visible (lazy loading) -->
<ReactCounter client:visible />
<!-- Load when browser is idle -->
<ReactCounter client:idle />
<!-- Load on media query match -->
<ReactCounter client:media="(max-width: 768px)" />
<!-- Only render on client, not server -->
<ReactCounter client:only="react" />
Example: Interactive Component
React Counter (needs JavaScript):
// src/components/ReactCounter.jsx
import { useState } from 'react';
export default function ReactCounter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
Use with selective hydration:
---
import ReactCounter from '../components/ReactCounter.jsx';
---
<h1>My Page</h1>
<p>This is static content - no JavaScript needed.</p>
<!-- Only this component loads React -->
<ReactCounter client:visible />
<p>More static content below...</p>
Result: Only the counter loads JavaScript, the rest is pure HTML!
π Integrations
Astro has official integrations for popular tools:
Add an Integration
# React
npx astro add react
# Vue
npx astro add vue
# Svelte
npx astro add svelte
# Tailwind CSS
npx astro add tailwind
# Partytown (third-party scripts)
npx astro add partytown
# Sitemap
npx astro add sitemap
Manual Configuration
// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import tailwind from '@astrojs/tailwind';
export default defineConfig({
integrations: [
react(),
tailwind(),
],
});
Popular Integrations
- UI Frameworks: React, Vue, Svelte, Solid, Preact, Lit
- CSS: Tailwind, UnoCSS
- CMS: Strapi, Contentful, Sanity, WordPress
- Deployment: Vercel, Netlify, Cloudflare Pages
- SEO: Sitemap, RSS feeds
- Analytics: Google Analytics, Plausible, Fathom
π¨ Styling
Scoped Styles (Default)
<button class="primary">Click me</button>
<style>
/* Only applies to this component */
.primary {
background: blue;
color: white;
}
</style>
Global Styles
<style is:global>
/* Applies everywhere */
body {
font-family: system-ui;
}
</style>
Import External CSS
---
import '../styles/global.css';
---
Tailwind CSS
npx astro add tailwind
<button class="bg-blue-500 text-white px-4 py-2 rounded">
Click me
</button>
CSS Modules
---
import styles from './styles.module.css';
---
<div class={styles.container}>
<h1 class={styles.title}>Hello</h1>
</div>
π Deployment
Build for Production
npm run build
This creates an optimized site in ./dist/ folder.
Deploy to Vercel
# Install Vercel CLI
npm i -g vercel
# Deploy
vercel
Or connect your GitHub repo to Vercel for automatic deployments.
Deploy to Netlify
Option 1: Netlify CLI
# Install
npm install netlify-cli -g
# Deploy
netlify deploy --prod
Option 2: Git integration
- Push code to GitHub
- Connect repo in Netlify dashboard
- Build command:
npm run build - Publish directory:
dist
Deploy to Cloudflare Pages
# Install Wrangler
npm install -g wrangler
# Login
wrangler login
# Deploy
wrangler pages publish dist
Other Platforms
Astro works with any static hosting:
- GitHub Pages
- AWS S3 + CloudFront
- Google Cloud Storage
- Azure Static Web Apps
- Railway
- Render
β Best Practices
1. Use Content Collections for Markdown
Instead of manually importing files, use Content Collections for type safety and better DX.
2. Optimize Images
---
import { Image } from 'astro:assets';
import heroImage from '../assets/hero.jpg';
---
<Image
src={heroImage}
alt="Hero"
width={1200}
height={600}
format="webp"
/>
3. Minimize Client-Side JavaScript
Only use client:* directives when absolutely necessary.
4. Use TypeScript
Enable TypeScript for better autocomplete and fewer bugs:
npm create astro@latest -- --typescript strict
5. Prefetch Links
<a href="/about" data-astro-prefetch>About</a>
6. Environment Variables
Use .env files and prefix public variables with PUBLIC_.
7. Component Organization
src/
βββ components/
β βββ ui/ # Buttons, cards, etc.
β βββ layout/ # Header, footer, etc.
β βββ features/ # Feature-specific components
βββ layouts/
βββ pages/
8. Performance Checklist
- β
Use Astroβs
<Image>component - β
Lazy load images with
loading="lazy" - β Minimize client-side JavaScript
- β
Use
client:visiblefor below-the-fold components - β Enable Cloudflare/Vercel edge caching
- β Compress images before adding to project
- β Use WebP/AVIF formats
π Next Steps
Congratulations! You now have a solid foundation in Astro.js. Hereβs what to explore next:
Continue Learning
- Build a project: Start with a blog or portfolio
- Explore integrations: Try React, Vue, or Svelte components
- Add a CMS: Connect to Contentful, Sanity, or Strapi
- Optimize SEO: Add meta tags, structured data, sitemap
- Deploy: Get your site live on Vercel or Netlify
Resources
- Official Astro Docs - Comprehensive documentation
- Astro Discord - Join the community
- Astro Themes - Pre-built templates
- Astro Blog - Latest updates and tutorials
- GitHub Examples - Official examples
Build Something Amazing
You now have everything you need to build lightning-fast websites with Astro. The best way to learn is by building!
Need help building your next project? Our web development team specializes in Astro.js and can help you create high-performance websites that convert.
Happy coding! π