Step 1: Understanding the Basics of Astro
What is Astro? Astro is a modern web framework that allows you to build faster websites with less client-side JavaScript. It focuses on content-first websites and follows a “islands architecture” approach, where interactive components are isolated while the rest of your site remains static HTML.
Key Features:
- Server-first architecture
- Zero JS by default (client-side JS is opt-in)
- Component islands
- Supports multiple UI frameworks (React, Vue, Svelte, etc.)
- Built-in file-based routing
- Excellent for content-heavy sites
Step 2: Setting Up Your Development Environment
-
Install Node.js (v16.12.0 or higher)
- Download from nodejs.org
- Verify installation:
node -vandnpm -v
-
Create Your First Astro Project
# Create a new project with the CLI npm create astro@latest # Follow the prompts to set up your project # Choose a starter template (recommend "Empty" for beginners) # Navigate to your project directory cd your-project-name # Start the development server npm run dev -
Explore Your Project Structure
src/: Main source codesrc/pages/: File-based routingsrc/components/: Reusable UI componentssrc/layouts/: Page layoutspublic/: Static assets
Step 3: Understanding Astro Pages and Routing
-
Create Basic Pages
- Add a page at
src/pages/about.astro
--- // Front matter (JavaScript/TypeScript code) const pageTitle = "About Me"; --- <!-- HTML markup --> <html> <head> <title>{pageTitle}</title> </head> <body> <h1>{pageTitle}</h1> <p>Welcome to my Astro site!</p> </body> </html> - Add a page at
-
File-Based Routing
index.astro→/about.astro→/aboutblog/index.astro→/blogblog/[slug].astro→ Dynamic routes like/blog/post-1
Step 4: Creating Layouts and Components
-
Create a Layout
- Add a file at
src/layouts/MainLayout.astro
--- const { title } = Astro.props; --- <html> <head> <title>{title}</title> </head> <body> <nav> <a href="/">Home</a> <a href="/about">About</a> <a href="/blog">Blog</a> </nav> <main> <slot /> <!-- Content is injected here --> </main> <footer> <p>© 2025 My Astro Site</p> </footer> </body> </html> - Add a file at
-
Use the Layout
- Update
src/pages/index.astro
--- import MainLayout from '../layouts/MainLayout.astro'; --- <MainLayout title="Home Page"> <h1>Welcome to my website!</h1> <p>This is my awesome Astro site.</p> </MainLayout> - Update
-
Create a Reusable Component
- Add a file at
src/components/Card.astro
--- const { title, description, url } = Astro.props; --- <div class="card"> <h2>{title}</h2> <p>{description}</p> <a href={url}>Read more</a> </div> <style> .card { border: 1px solid #ddd; padding: 1rem; border-radius: 4px; } </style> - Add a file at
-
Use the Component
- In any page:
--- import MainLayout from '../layouts/MainLayout.astro'; import Card from '../components/Card.astro'; --- <MainLayout title="Blog"> <h1>Blog Posts</h1> <Card title="First Post" description="This is my first blog post" url="/blog/first-post" /> </MainLayout>
Step 5: Styling Your Astro Site
-
Component-Level Styles
<style> h1 { color: navy; } /* Styles are automatically scoped to the component */ </style> -
Global Styles
- Create
src/styles/global.css
* { box-sizing: border-box; margin: 0; } body { font-family: sans-serif; line-height: 1.6; }- Import in your layout:
--- import '../styles/global.css'; --- - Create
-
CSS Frameworks
- Install Tailwind CSS:
npx astro add tailwind- Follow the setup instructions that appear
Step 6: Adding Content with Markdown
-
Create Markdown Files
- Add a file at
src/pages/blog/first-post.md
--- title: My First Blog Post pubDate: 2025-03-24 description: This is my first blog post! author: Your Name --- # My First Blog Post This is my **first** blog post using *Astro*. It's really cool! ## Section One - List item one - List item two - Add a file at
-
Create a Layout for Blog Posts
- Add
src/layouts/BlogPostLayout.astro
--- import MainLayout from './MainLayout.astro'; const { frontmatter } = Astro.props; --- <MainLayout title={frontmatter.title}> <article> <h1>{frontmatter.title}</h1> <p>Published on: {frontmatter.pubDate.slice(0,10)}</p> <p><em>{frontmatter.description}</em></p> <slot /> </article> </MainLayout> - Add
-
Apply the Layout to Markdown Files
- Update
src/pages/blog/first-post.md
--- title: My First Blog Post pubDate: 2025-03-24 description: This is my first blog post! author: Your Name layout: ../../layouts/BlogPostLayout.astro --- # My First Blog Post ... - Update
Step 7: Dynamic Content with the Content Collections API
-
Set Up Content Collections
- Create
src/content/config.ts
import { defineCollection, z } from 'astro:content'; const blogCollection = defineCollection({ schema: z.object({ title: z.string(), pubDate: z.date(), description: z.string(), author: z.string(), }), }); export const collections = { 'blog': blogCollection, }; - Create
-
Create Blog Posts in the Collection
- Move blog posts to
src/content/blog/first-post.md
- Move blog posts to
-
Query and Render Collection Data
- Update
src/pages/blog/index.astro
--- import MainLayout from '../../layouts/MainLayout.astro'; import { getCollection } from 'astro:content'; const blogPosts = await getCollection('blog'); --- <MainLayout title="Blog"> <h1>All Blog Posts</h1> <ul> {blogPosts.map(post => ( <li> <a href={`/blog/${post.slug}`}>{post.data.title}</a> <p>{post.data.description}</p> </li> ))} </ul> </MainLayout> - Update
-
Create a Dynamic Route for Blog Posts
- Add
src/pages/blog/[...slug].astro
--- import { getCollection } from 'astro:content'; import BlogPostLayout from '../../layouts/BlogPostLayout.astro'; export async function getStaticPaths() { const blogEntries = await getCollection('blog'); return blogEntries.map(entry => ({ params: { slug: entry.slug }, props: { entry }, })); } const { entry } = Astro.props; const { Content } = await entry.render(); --- <BlogPostLayout frontmatter={entry.data}> <Content /> </BlogPostLayout> - Add
Step 8: Adding Interactivity with Islands
-
Create a React Component (or Vue/Svelte/etc.)
- Install the integration:
npx astro add react - Create
src/components/Counter.jsx
import { useState } from 'react'; export default function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); } - Install the integration:
-
Use the Component as an Island
- Add to a page:
--- import MainLayout from '../layouts/MainLayout.astro'; import Counter from '../components/Counter.jsx'; --- <MainLayout title="Interactive Demo"> <h1>Interactive Component</h1> <Counter client:load /> </MainLayout> -
Client Directives
client:load: Load and hydrate the component on page loadclient:idle: Hydrate when the browser is idleclient:visible: Hydrate when the component is visibleclient:media: Hydrate when a media query is matched
Step 9: Fetching External Data
- Fetch Data in the Front Matter
--- import MainLayout from '../layouts/MainLayout.astro'; // Fetch data at build time const response = await fetch('https://jsonplaceholder.typicode.com/users'); const users = await response.json(); --- <MainLayout title="Users"> <h1>Users List</h1> <ul> {users.map(user => ( <li>{user.name} - {user.email}</li> ))} </ul> </MainLayout>
Step 10: Optimizing Your Site
-
Image Optimization
- Use the built-in Image component:
--- import { Image } from 'astro:assets'; import myImage from '../assets/my-image.jpg'; --- <Image src={myImage} alt="Description" width="600" height="400" /> -
SEO Optimization
- Add meta tags in your layout
- Consider installing
astro-seopackage - Create a reusable SEO component
Step 11: Deploying Your Astro Site
-
Build Your Site
npm run build- Output is in the
dist/directory
- Output is in the
-
Choose a Hosting Platform
- Netlify:
npx astro add netlify - Vercel:
npx astro add vercel - GitHub Pages:
npx astro add github
- Netlify:
-
Deploy
- Follow the platform-specific instructions
- Example for Netlify:
netlify deploy --prod
Step 12: Advanced Astro Features
-
Server-Side Rendering (SSR)
- Enable SSR:
npx astro add node - Update
astro.config.mjs:
export default defineConfig({ output: 'server', }); - Enable SSR:
-
API Routes
- Create
src/pages/api/hello.js:
export function GET() { return new Response(JSON.stringify({ message: "Hello, World!" }), { headers: { "Content-Type": "application/json" } }); } - Create
-
Middleware
- Create
src/middleware.js:
export function onRequest({ request, locals }, next) { // Run code before the request is processed locals.customData = "example"; return next(); } - Create
Resources for Continued Learning
- Official Documentation: docs.astro.build
- Astro Discord Community: astro.build/chat
- Example Projects: astro.new
- Astro Blog: astro.build/blog
Start with small projects to practice what you’ve learned, and gradually build more complex applications as you become more comfortable with Astro’s features and capabilities.