A beginners guide to Astro.js for static sites and blogs
Getting Started with Astroh1
Hello! I’m Ahmet Zeybek, a full stack developer passionate about building fast, efficient web applications. Recently, I migrated my personal blog to Astro.js, and it transformed my workflow. If you’re tired of heavy JavaScript bundles for simple sites, Astro is a breath of fresh air. In this guide, I’ll walk you through why I chose Astro, how to set it up, key features like content collections and islands, and tips for optimal performance. Whether you’re building a blog, portfolio, or docs site, Astro makes it simple and speedy.
Why Astro?h2
Astro is an all-in-one static site builder designed for content-driven websites. Unlike traditional frameworks like Next.js or Gatsby that ship large JS bundles, Astro prioritizes performance with its “zero JavaScript by default” philosophy. Here’s what hooked me:
- Static-First Rendering: Pages are pre-built as HTML at build time, leading to lightning-fast loads (often under 100ms).
- Islands Architecture: Interactive components (e.g., a React search bar) are “islands” that hydrate only when needed – no full-page JS.
- Framework Agnostic: Mix React, Vue, Svelte, or even vanilla JS in the same project.
- MDX Support: Write content in Markdown with embedded JSX for dynamic elements like components or embeds.
- Built-in Optimizations: Automatic image optimization, CSS minification, and partial hydration.
For my blog, Astro reduced my bundle size from 500KB to ~10KB while keeping interactive features like animations. It’s perfect for developers who want control without bloat.
Setup Stepsh2
Getting started is straightforward. Prerequisites: Node.js 18+ and a package manager like pnpm or npm.
1. Create a New Projecth3
Run the CLI to scaffold:
npm create astro@latest my-astro-blogcd my-astro-blognpm installThis sets up a basic structure with src/pages/, src/components/, and astro.config.mjs.
2. Add Integrationsh3
Astro shines with plugins. For a blog, add Tailwind for styling and React for components:
import { defineConfig } from 'astro/config'import react from '@astrojs/react'import tailwind from '@astrojs/tailwind'
export default defineConfig({ integrations: [react(), tailwind()], markdown: { shikiConfig: { theme: 'github-dark', // Or your preferred theme }, },})Install dependencies:
npx astro add react tailwind3. Run Development Serverh3
npm run devVisit http://localhost:4321 – your site is live with hot reloading!
4. Build and Deployh3
npm run build # Outputs to /distnpm run preview # Local preview of buildDeploy to Vercel, Netlify, or GitHub Pages with one command – Astro generates static files.
Content Collections: Structured Datah2
Astro’s content collections make managing blog posts easy. Define schemas for validation and querying.
Setuph3
In src/content/config.ts:
import { z, defineCollection } from 'astro:content'
const blogCollection = defineCollection({ schema: z.object({ title: z.string(), description: z.string(), pubDate: z.date(), tags: z.array(z.string()), author: z.string().default('Ahmet Zeybek'), cover: z .object({ src: z.string(), alt: z.string(), }) .optional(), pinned: z.boolean().default(false), }),})
export const collections = { blog: blogCollection,}Creating a Posth3
Posts go in src/content/blog/my-post.mdx:
---title: 'My Astro Journey'description: 'First steps with Astro'pubDate: 2024-10-02tags: ['Astro', 'Blogging']cover: src: '/images/astro-cover.jpg' alt: 'Astro logo'---
# My Astro Journey
This is my first MDX post in Astro. It's mixing Markdown with JSX!
<Alert type="info">Astro rocks!</Alert>Querying Collectionsh3
In a page (src/pages/blog/index.astro):
---import { getCollection } from 'astro:content'
const posts = await getCollection('blog')const sortedPosts = posts.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf())---
<ul> { sortedPosts.map((post) => ( <li> <a href={`/blog/${post.slug}`}>{post.data.title}</a> <p>{post.data.description}</p> </li> )) }</ul>This auto-generates slugs and provides type safety.
Islands and MDX: Interactive Contenth2
Islands Architectureh3
Islands allow partial hydration. For example, embed a React counter in a static page:
<html> <body> <h1>Static Content</h1> <p>This is pre-rendered HTML.</p>
<!-- Island: Hydrates only this part --> <MyReactCounter client:load /> </body></html>MyReactCounter.jsx (in src/components/):
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> )}Only the counter JS loads – the rest is static!
MDX for Rich Postsh3
MDX lets you embed components in Markdown. Install @astrojs/mdx:
npx astro add mdxIn a post:
# Hello MDX
This is Markdown.
import { MyComponent } from '../components/MyComponent.jsx'
<MyComponent message="From MDX!" />Transforms into dynamic content without full JS.
Code Snippets: Pages and Configh2
Custom Page with Layouth3
src/layouts/BlogLayout.astro:
---// Props for dynamic titleconst { title } = Astro.props---
<html> <head> <title>{title}</title> </head> <body> <header> <nav>Blog Nav</nav> </header> <main> <slot /> <!-- Page content here --> </main> <footer>Footer</footer> </body></html>Use in page:
---import BlogLayout from '../../layouts/BlogLayout.astro'import { getEntry } from 'astro:content'
const { slug } = Astro.paramsconst post = await getEntry('blog', slug)const { Content } = await post.render()---
<BlogLayout title={post.data.title}> <article> <h1>{post.data.title}</h1> <Content /> </article></BlogLayout>Astro Config for SEOh3
astro.config.mjs:
export default defineConfig({ site: 'https://yourblog.com', integrations: [react(), tailwind()], vite: { ssr: { noExternal: ['some-lib'], // SSR for specific packages }, }, markdown: { remarkPlugins: [remarkMath], rehypePlugins: [rehypeKatex], },})Performance Tipsh2
Astro is fast out-of-the-box, but optimize further:
- Images: Use
astro:assetsfor optimization – auto WebP, lazy loading. - Critical CSS: Inline above-the-fold styles.
- Preload Fonts:
<link rel="preload" as="font" ...>. - Search: Integrate Pagefind for client-side search without JS bloat.
- Analytics: Add Plausible or Google Analytics as islands.
Benchmark: My blog scores 100/100 on Lighthouse after Astro migration.
Pitfall: Overusing islands – keep static where possible.
Resources for Further Learningh2
- Official Docs: Astro Docs
- Tutorials: freeCodeCamp Astro course, YouTube “Astro in 100 Seconds”.
- Communities: Astro Discord, Reddit r/astrojs.
- Books/Tools: “Static Site Generators” guides; VS Code Astro extension.
- My Project: Check this blog’s source on GitHub for real-world examples.
Conclusionh2
Astro has revolutionized how I build static sites – faster, simpler, and more fun. From setup to deployment, it’s developer-friendly without sacrificing power. If you’re starting a blog or portfolio, give Astro a try – you won’t go back. What’s your first Astro project? Share in the comments!