Astro Quick Tutorial for Vue Developers
Astro is a modern web framework that focuses on **content-driven websites** with **zero JS by default**. Unlike Vue's SPA approach, Astro uses **islands archite
Astro frontend web development tutorial Vue
What is Astro?
Astro is a modern web framework that focuses on content-driven websites with zero JS by default. Unlike Vueβs SPA approach, Astro uses islands architecture - you ship HTML/CSS by default and add JavaScript only where needed.
Key Concepts Coming from Vue
1. Component Structure
---// Component Script (runs at build time)const title = 'Hello World';const items = ['Vue', 'React', 'Svelte'];---
<!-- Template (similar to Vue template) --><h1>{title}</h1><ul> {items.map((item) => <li>{item}</li>)}</ul>
<style> h1 { color: blue; }</style>Key Differences from Vue:
- Frontmatter: Code between
---runs at build time (server-side) - JSX-like syntax: Use
{}instead of{{ }}for expressions - No reactivity by default: Variables are static unless you add client-side JS
2. File-based Routing
src/pages/βββ index.astro β /βββ about.astro β /aboutβββ blog/β βββ index.astro β /blogβ βββ [slug].astro β /blog/hello-worldβββ api/ βββ posts.json.js β /api/posts.json3. Layouts (Similar to Vue Router layouts)
---const { title } = Astro.props;---
<!-- src/layouts/BaseLayout.astro --><html> <head> <title>{title}</title> </head> <body> <nav>...</nav> <main> <slot /> </main> </body></html>---import BaseLayout from '../layouts/BaseLayout.astro';---
<!-- src/pages/about.astro --><BaseLayout title="About"> <h1>About Page</h1></BaseLayout>4. Using Vue Components in Astro
---import VueCounter from '../components/VueCounter.vue';---
<!-- Static by default --><VueCounter count={5} />
<!-- Interactive (hydrated) --><VueCounter count={5} client:load /><VueCounter count={5} client:idle /><VueCounter count={5} client:visible />Client Directives:
client:load- Hydrate immediatelyclient:idle- Hydrate when main thread is freeclient:visible- Hydrate when component is visibleclient:media- Hydrate when media query matches
5. Data Fetching (Build-time)
---// This runs at build time, similar to Vue's asyncDataconst response = await fetch('https://api.example.com/posts');const posts = await response.json();---
<div> { posts.map((post) => ( <article> <h2>{post.title}</h2> <p>{post.excerpt}</p> </article> )) }</div>6. Dynamic Routes
---export async function getStaticPaths() { const posts = await fetchPosts(); return posts.map((post) => ({ params: { slug: post.slug }, props: { post }, }));}
const { post } = Astro.props;---
<!-- src/pages/blog/[slug].astro --><h1>{post.title}</h1><p>{post.content}</p>7. Content Collections (for Markdown/MDX)
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({ type: 'content', schema: z.object({ title: z.string(), date: z.date(), tags: z.array(z.string()), }),});
export const collections = { blog };---import { getCollection } from 'astro:content';const posts = await getCollection('blog');---Migration Tips from Vue
1. Mindset Shift
- Vue: Runtime, reactive, SPA
- Astro: Build-time, static-first, MPA
2. State Management
- Use Vue components with
client:*directives for reactive state - Consider nanostores for shared state between islands
3. Styling
---// Can import CSS modules, Sass, etc.import styles from './Component.module.css';---
<div class={styles.container}> <!-- Scoped styles work similar to Vue --> <style> .local-class { color: red; } </style>
<!-- Global styles --> <style is:global> body { font-family: Arial; } </style></div>4. Environment Variables
// Access like Vue's process.envconst apiUrl = import.meta.env.PUBLIC_API_URL;const secret = import.meta.env.SECRET_KEY; // Only available server-sideProject Setup
# Create new Astro projectnpm create astro@latest my-project
# Add Vue integrationnpx astro add vue
# Add other integrationsnpx astro add tailwindnpx astro add mdxConfiguration
import { defineConfig } from 'astro/config';import vue from '@astrojs/vue';
export default defineConfig({ integrations: [vue()], output: 'static', // or 'server' for SSR});Common Patterns
Mixed Static + Interactive
---// Static data fetchingconst products = await fetchProducts();---
<!-- Static list --><div class="products"> { products.map((product) => ( <div class="product-card"> <h3>{product.name}</h3> <p>{product.price}</p> {/* Interactive component */} <AddToCart product={product} client:visible /> </div> )) }</div>API Routes
export async function POST({ request }) { const data = await request.formData(); const email = data.get('email');
// Process signup await addToNewsletter(email);
return new Response(JSON.stringify({ success: true }), { status: 200, headers: { 'Content-Type': 'application/json' }, });}Best Practices
- Start static, add interactivity where needed
- Use Vue components for complex interactive features
- Leverage Astroβs build-time data fetching
- Consider SEO benefits of server-rendered HTML
- Use content collections for blog/documentation sites
Performance Benefits
- Smaller bundles: Only ship JS for interactive components
- Better Core Web Vitals: Less JavaScript = faster loading
- SEO-friendly: Server-rendered HTML by default
- Progressive enhancement: Site works without JavaScript
This architecture is perfect for content sites, marketing pages, blogs, and e-commerce where you want the performance benefits of static sites but the flexibility to add interactivity where needed.