Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds
Volver a colecciones
Guide8 min read

Astro

Astro is a modern web framework with Island Architecture that ships zero JavaScript by default, ensuring blazing-fast page loads.

Astro - Complete Guide to Island Architecture and Content-First Web Development

What is Astro and Why is it Different?

Astro is a modern web framework that revolutionizes how we build websites through its Island Architecture. The core philosophy is "ship less JavaScript"—by default, Astro pages send zero JavaScript to the browser, resulting in blazing-fast load times and excellent Core Web Vitals scores.

What makes Astro unique is its ability to use your favorite UI frameworks (React, Vue, Svelte, Solid) as isolated islands of interactivity in a sea of static HTML. You can have a React slider and a Vue form on the same page, loading JavaScript only where it's truly needed.

Why Choose Astro?

Astro vs Other Frameworks

FeatureAstroNext.jsGatsbySvelteKit
Default JS0 KB~80 KB+~50 KB+~15 KB
Multi-frameworkYesReact onlyReact onlySvelte only
Content focusBuilt-inPluginBuilt-inManual
Island ArchitectureNativeNoNoNo
Learning curveLowMediumHighMedium
Build timeFastMediumSlowFast

Key Advantages

  • Zero JavaScript by default - Pages are static HTML, JS loaded on demand
  • Island Architecture - Isolated interactive components
  • Framework agnostic - Use React, Vue, Svelte, Solid together
  • Content Collections - Type-safe content management
  • View Transitions - Smooth page transitions native
  • Excellent SEO - Static HTML = perfect indexing
  • Server Islands - Dynamic islands rendered on server

When to Choose Astro

  • Blogs and documentation - Perfect for content-first sites
  • Landing pages - Maximum performance and SEO
  • Portfolios - Fast, responsive sites
  • E-commerce (product pages) - Static pages + dynamic carts
  • Marketing sites - Excellent Core Web Vitals
  • Hybrid apps - Mostly static with interactive elements

Installation and Setup

Creating a New Project

Code
Bash
# Create new Astro project
npm create astro@latest

# Or with a starter template
npm create astro@latest -- --template blog
npm create astro@latest -- --template docs

Project Structure

Code
TEXT
my-astro-site/
├── src/
│   ├── components/     # UI components
│   ├── layouts/        # Page layouts
│   ├── pages/          # File-based routing
│   └── content/        # Content collections
├── public/             # Static assets
├── astro.config.mjs    # Astro configuration
└── package.json

Astro Components

Basic Component

Code
ASTRO
---
// Component Script (runs at build time)
const greeting = 'Hello, Astro!'
const items = ['React', 'Vue', 'Svelte']
---

<!-- Component Template -->
<div class="card">
  <h1>{greeting}</h1>
  <ul>
    {items.map((item) => <li>{item}</li>)}
  </ul>
</div>

<style>
  /* Scoped styles by default */
  .card {
    padding: 1rem;
    background: white;
    border-radius: 8px;
  }
</style>

Props and Slots

Code
ASTRO
---
interface Props {
  title: string
  description?: string
}

const { title, description = 'Default description' } = Astro.props
---

<article>
  <h2>{title}</h2>
  <p>{description}</p>
  <slot /> <!-- Children go here -->
  <slot name="footer" /> <!-- Named slot -->
</article>

Using Components

Code
ASTRO
---
import Card from '../components/Card.astro'
import Button from '../components/Button.astro'
---

<Card title="Welcome" description="Hello world">
  <p>This goes in the default slot</p>
  <Button slot="footer">Click me</Button>
</Card>

Pages and Routing

File-Based Routing

Code
TEXT
src/pages/
├── index.astro        → /
├── about.astro        → /about
├── blog/
│   ├── index.astro    → /blog
│   └── [slug].astro   → /blog/:slug (dynamic)
└── [...slug].astro    → catch-all route

Dynamic Routes

Code
ASTRO
---
// src/pages/blog/[slug].astro
export async function getStaticPaths() {
  const posts = await getCollection('blog')
  return posts.map((post) => ({
    params: { slug: post.slug },
    props: { post },
  }))
}

const { post } = Astro.props
---

<article>
  <h1>{post.data.title}</h1>
  <Content />
</article>

Island Architecture

What Are Islands?

Islands are interactive UI components on an otherwise static HTML page. They load JavaScript only when needed, keeping your site fast.

Framework Integrations

Code
Bash
# Add React
npx astro add react

# Add Vue
npx astro add vue

# Add Svelte
npx astro add svelte

# Add multiple at once
npx astro add react vue svelte

Using Framework Components

Code
ASTRO
---
import ReactCounter from '../components/Counter.jsx'
import VueForm from '../components/Form.vue'
import SvelteSlider from '../components/Slider.svelte'
---

<h1>Static HTML</h1>

<!-- React island - interactive -->
<ReactCounter client:load />

<!-- Vue island - interactive on visible -->
<VueForm client:visible />

<!-- Svelte island - interactive on idle -->
<SvelteSlider client:idle />

Client Directives

Code
ASTRO
<!-- Load immediately -->
<Counter client:load />

<!-- Load when browser is idle -->
<Counter client:idle />

<!-- Load when component is visible -->
<Counter client:visible />

<!-- Load on specific media query -->
<Counter client:media="(max-width: 768px)" />

<!-- Only load on client, skip SSR -->
<Counter client:only="react" />

Content Collections

Defining Collections

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

const blogCollection = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    author: z.string(),
    image: z.string().optional(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
  }),
})

export const collections = {
  blog: blogCollection,
}

Content Files

Code
Markdown
---
// src/content/blog/my-first-post.md
title: My First Post
description: Welcome to my blog
pubDate: 2024-01-15
author: John Doe
tags: [astro, web]
---

# Hello World

This is my first blog post written in Markdown!

Querying Collections

Code
ASTRO
---
import { getCollection, getEntry } from 'astro:content'

// Get all posts
const allPosts = await getCollection('blog')

// Filter posts
const publishedPosts = await getCollection('blog', ({ data }) => {
  return !data.draft
})

// Get single entry
const post = await getEntry('blog', 'my-first-post')
---

<ul>
  {publishedPosts.map((post) => (
    <li>
      <a href={`/blog/${post.slug}`}>{post.data.title}</a>
    </li>
  ))}
</ul>

Layouts

Creating a Layout

Code
ASTRO
---
// src/layouts/BaseLayout.astro
interface Props {
  title: string
  description?: string
}

const { title, description = 'My Astro Site' } = Astro.props
---

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <meta name="description" content={description} />
    <title>{title}</title>
  </head>
  <body>
    <header>
      <nav>
        <a href="/">Home</a>
        <a href="/blog">Blog</a>
        <a href="/about">About</a>
      </nav>
    </header>
    <main>
      <slot />
    </main>
    <footer>
      <p>&copy; 2024 My Site</p>
    </footer>
  </body>
</html>

Using Layouts

Code
ASTRO
---
import BaseLayout from '../layouts/BaseLayout.astro'
---

<BaseLayout title="Home Page">
  <h1>Welcome!</h1>
  <p>This content goes in the slot.</p>
</BaseLayout>

View Transitions

Code
ASTRO
---
// src/layouts/BaseLayout.astro
import { ViewTransitions } from 'astro:transitions'
---

<html>
  <head>
    <ViewTransitions />
  </head>
  <body>
    <slot />
  </body>
</html>
Code
ASTRO
<!-- Animate specific elements -->
<h1 transition:name="title">Page Title</h1>

<img
  src="/hero.jpg"
  transition:name="hero"
  transition:animate="fade"
/>

<!-- Animation options: fade, slide, none -->
<div transition:animate="slide">Content</div>

API Routes (Server Endpoints)

TSsrc/pages/api/posts.ts
TypeScript
// src/pages/api/posts.ts
import type { APIRoute } from 'astro'

export const GET: APIRoute = async ({ params, request }) => {
  const posts = await getPosts()
  return new Response(JSON.stringify(posts), {
    headers: { 'Content-Type': 'application/json' },
  })
}

export const POST: APIRoute = async ({ request }) => {
  const data = await request.json()
  const post = await createPost(data)
  return new Response(JSON.stringify(post), { status: 201 })
}

Hybrid Rendering

SSR Configuration

astro.config.mjs
JavaScript
// astro.config.mjs
import { defineConfig } from 'astro/config'
import node from '@astrojs/node'

export default defineConfig({
  output: 'hybrid', // or 'server' for full SSR
  adapter: node({
    mode: 'standalone',
  }),
})

Per-Page Rendering

Code
ASTRO
---
// Force static generation
export const prerender = true
---
Code
ASTRO
---
// Force server rendering (in hybrid mode)
export const prerender = false

const user = await getUser(Astro.cookies.get('session'))
---

Styling Options

Scoped Styles

Code
ASTRO
<style>
  /* Scoped to this component only */
  h1 {
    color: blue;
  }
</style>

Global Styles

Code
ASTRO
<style is:global>
  /* Applies globally */
  body {
    font-family: system-ui;
  }
</style>

Tailwind CSS

Code
Bash
npx astro add tailwind
Code
ASTRO
<div class="flex items-center gap-4 p-6 bg-white rounded-lg shadow">
  <h1 class="text-2xl font-bold text-gray-900">Hello</h1>
</div>

Image Optimization

Code
ASTRO
---
import { Image } from 'astro:assets'
import heroImage from '../images/hero.png'
---

<!-- Optimized image -->
<Image
  src={heroImage}
  alt="Hero image"
  width={800}
  height={400}
/>

<!-- Remote image -->
<Image
  src="https://example.com/image.jpg"
  alt="Remote image"
  width={800}
  height={400}
  inferSize
/>

Deployment

Static (Default)

Code
Bash
# Build static site
npm run build

# Output in dist/ folder
# Deploy to any static host: Netlify, Vercel, GitHub Pages

Vercel

Code
Bash
npx astro add vercel

Netlify

Code
Bash
npx astro add netlify

Cloudflare Pages

Code
Bash
npx astro add cloudflare

Best Practices

Performance

Code
ASTRO
<!-- Use client directives wisely -->
<!-- client:load - Only for above-the-fold interactive content -->
<!-- client:visible - Preferred for below-the-fold content -->
<!-- client:idle - For non-critical interactivity -->

<!-- Prefer Astro components for static content -->
<Card title="Static" />

<!-- Use framework components only when needed -->
<ReactCounter client:visible />

Content Organization

Code
TEXT
src/content/
├── blog/           # Blog posts
│   ├── post-1.md
│   └── post-2.md
├── docs/           # Documentation
│   ├── intro.md
│   └── guides/
└── config.ts       # Collection schemas

Summary

Astro is the ideal framework for content-focused websites:

  • Zero JavaScript by default - Maximum performance out of the box
  • Island Architecture - JavaScript only where needed
  • Multi-framework - Use React, Vue, Svelte, Solid together
  • Content Collections - Type-safe content management
  • View Transitions - Native smooth page transitions
  • Excellent SEO - Static HTML = perfect indexing
  • Hybrid rendering - Static + SSR when needed

Astro combines the performance benefits of static site generation with the flexibility of interactive islands, making it perfect for blogs, documentation, marketing sites, and any content-first web project.