Rebuilding Your Blog with Astro: A Step-by-Step Guide
Introduction
Astro is a modern Static Site Generator (SSG) gaining popularity for its innovative features and performance benefits. Let’s dive into the setup process and explore how to rebuild your blog using Astro.
Features of Astro
Astro simplifies web development by focusing on performance and flexibility. Two standout features are:
- Astro Islands: Isolates components into islands, enabling you to use multiple frameworks like React, Vue within the same project.
- Zero JS, by default: Optimizes performance by converting most JavaScript to pure HTML at build time.
For detailed explanations, visit Astro’s official documentation.
Setting Up Astro
Astro makes the setup process seamless. Follow these steps to kickstart your Astro blog:
1. Initializing the Project
Run the following command to create a new Astro project:
npm create astro@latest
2. Configuration Prompts
During the setup, you will be prompted to make choices for your project. Below is an example walkthrough:
astro Launch sequence initiated.
dir Where should we create your new project?
./
tmpl How would you like to start your new project?
Use blog template
██████ Template copying...
deps Install dependencies?
Yes
██████ Installing dependencies with npm...
ts Do you plan to write TypeScript?
Yes
use How strict should TypeScript be?
Strict
██████ TypeScript customizing...
git Initialize a new git repository?
Yes
██████ Git initializing...
next Liftoff confirmed. Explore your project!
Run npm run dev to start the dev server. CTRL+C to stop.
Add frameworks like react or tailwind using astro add.
Stuck? Join us at https://astro.build/chat
╭─────╮ Houston:
│ ◠ ◡ ◠ Good luck out there, astronaut! 🚀
╰─────╯
3. Starting the Development Server
After the setup is complete, start the development server:
npm run dev
The default server will be available at http://localhost:4321/
.
Content Collections
Overview
Contents such as blog posts are managed as collections in Astro. For a comprehensive guide, refer to the official page on Content Collections.
Create any directory under src/content/
and place Markdown files inside it. MDX is also supported. Below is an example of a blog post file (blog/first-post.md
):
---
title: 'First post'
description: 'Lorem ipsum dolor sit amet'
pubDate: 'Jul 08 2022'
heroImage: '/blog-placeholder-3.jpg'
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
...
Collection Definition
Astro allows users to define metadata (frontmatter) and types for content within src/content/config.ts
using the Zod schema validation library. Although optional, defining collections is highly recommended for its benefits, such as schema validation and TypeScript typings.
The
src/content/config.ts
file is optional. However, choosing not to define your collections will disable some of their best features, like frontmatter schema validation or automatic TypeScript typings.
Here is an example of a basic config.ts
setup:
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
// Type-check frontmatter using a schema
schema: z.object({
title: z.string(),
description: z.string(),
// Transform string to Date object
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
heroImage: z.string().optional(),
}),
});
export const collections = { blog };
Key | Type | Mandatory |
---|---|---|
title | string | Yes |
description | string | Yes |
pubDate | Date | Yes |
updatedDate | Date | No |
heroImage | string | No |
For more details on defining collections, see the official guide.
Using Collections
Astro provides functions such as getCollection
and getEntry
to access content collections.
For instance, frontmatter is stored within the data
object, allowing easy access:
---
import { getEntry } from 'astro:content';
const blogPost = await getEntry('blog', 'welcome');
---
<h1>{blogPost.data.title}</h1>
<p>{blogPost.data.description}</p>
By defining and using collections effectively, developers can simplify content management while ensuring a consistent structure across their projects.
Pages
Routing
Astro uses file-based routing, which means files in the pages
directory automatically map to specific URLs. Here’s an example of how the routing works:
File | URL |
---|---|
pages/index.astro | https://<YOUR_DOMAIN>/ |
pages/about.astro | https://<YOUR_DOMAIN>/about/ |
pages/blog/index.astro | https://<YOUR_DOMAIN>/blog/ |
Additionally, dynamic routing is supported. This is achieved by using [...NAME]
as a directory or file name. For example, in a newly set up project, blog/[...slug].astro
is configured for dynamic routing.
The [...slug]
part is handled by the getStaticPaths
function within the file, as seen in the following example:
---
import { type CollectionEntry, getCollection } from 'astro:content';
import BlogPost from '../../layouts/BlogPost.astro';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post) => ({
params: { slug: post.slug },
props: post,
}));
}
type Props = CollectionEntry<'blog'>;
const post = Astro.props;
const { Content } = await post.render();
---
<BlogPost {...post.data}>
<Content />
</BlogPost>
Page Structure
Astro’s pages, layouts, and components follow a structure familiar to modern frontend setups. Components can also be placed directly within pages, but the recommended approach is to keep reusable elements in the components
directory.
Below is an example of a typical page structure:
src/
├── components/
│ ├── BaseHead.astro
│ ├── Header.astro
│ └── Footer.astro
├── layouts/
│ └── BlogPost.astro
├── pages/
│ ├── index.astro
│ ├── about.astro
│ └── blog/
│ ├── index.astro
│ └── [...slug].astro
Astro Components
Astro components are written using a syntax similar to React’s JSX/TSX. If you are familiar with React, transitioning to Astro’s syntax should feel natural. Here is a snippet of the pages/index.astro
file:
---
import BaseHead from "../components/BaseHead.astro";
import Header from "../components/Header.astro";
import Footer from "../components/Footer.astro";
import { SITE_TITLE, SITE_DESCRIPTION } from "../consts";
// This will cause an error.
// const main = document.querySelector('main');
---
<!doctype html>
<html lang="en">
<head>
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
</head>
<body>
<Header title={SITE_TITLE} />
<main>
<h1>🧑🚀 Hello, Astronaut!</h1>
<!-- Additional content -->
</main>
<Footer />
<script>
// Use document, windoww, etc. here.
const main = document.querySelector('main');
</script>
</body>
</html>
Important Considerations
Astro’s frontmatter (---
block) is processed on the server using Node.js. This means that browser-specific objects like document
or window
are not available in the frontmatter. Attempting to use them there will result in an error:
error document is not defined
Hint:
Browser APIs are not available on the server.
Move your code to a <script> tag outside of the frontmatter, so the code runs on the client.
See https://docs.astro.build/en/guides/troubleshooting/#document-or-window-is-not-defined for more information.
For browser-based operations, ensure they are wrapped in <script>
tags, as shown in the example above.
Rendering Content
To render content dynamically, Astro provides the Content
component, which can be extracted from CollectionEntry
. In the example below, Content
is rendered within the pages/blog/[...slug].astro
file:
---
import { type CollectionEntry, getCollection } from 'astro:content';
import BlogPost from '../../layouts/BlogPost.astro';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post) => ({
params: { slug: post.slug },
props: post,
}));
}
type Props = CollectionEntry<'blog'>;
const post = Astro.props;
const { Content } = await post.render();
---
<BlogPost {...post.data}>
<Content />
</BlogPost>
Alternatively, you can directly import Markdown files and use them as components:
---
import { Content as PromoBanner } from '../components/promoBanner.md';
---
<h2>Today's promo</h2>
<PromoBanner />
Astro’s combination of static and dynamic routing, flexible component system, and intuitive rendering process makes it a powerful tool for building scalable and performant websites.
Building
Running npm run build
or astro build
will generate build artifacts in the dist/
directory. These artifacts represent your fully built site, ready for deployment. Below is an example of the generated output right after setup:
As you can see, no JavaScript is generated, keeping the build lightweight and highly optimized.
Example Build Output
tree dist/ --dirsfirst
dist/
├── about
│ └── index.html
├── blog
│ ├── first-post
│ │ └── index.html
│ ├── markdown-style-guide
│ │ └── index.html
│ ├── second-post
│ │ └── index.html
│ ├── third-post
│ │ └── index.html
│ ├── using-mdx
│ │ └── index.html
│ └── index.html
├── fonts
│ ├── atkinson-bold.woff
│ └── atkinson-regular.woff
├── blog-placeholder-1.jpg
├── blog-placeholder-2.jpg
├── blog-placeholder-3.jpg
├── blog-placeholder-4.jpg
├── blog-placeholder-5.jpg
├── blog-placeholder-about.jpg
├── favicon.svg
├── index.html
├── rss.xml
├── sitemap-0.xml
└── sitemap-index.xml
Key Insights
- HTML Output: Each page is pre-rendered into static HTML files.
- No JavaScript: Astro minimizes JavaScript unless explicitly added via frameworks or components.
- Static Assets: Fonts and images are included as-is, ensuring no unnecessary processing for public resources.
Astro’s build process prioritizes speed and efficiency, making it ideal for performance-critical websites.
Other Topics
Image Component
Astro provides an Image
component for optimized image handling. Images placed under the src/
directory can be transformed and converted to WebP format during the build process, improving performance.
Recommendations
- Place images under
src/
for optimization. - Avoid using
public/
for local images if transformations are required.
For more information, refer to the Astro Images Guide.
Tailwind CSS Integration
Astro supports Tailwind CSS through its integration system. To install Tailwind, run:
npx astro add tailwind
This process:
- Installs
@astrojs/tailwind
and the requiredtailwindcss
package. - Generates a minimal
tailwind.config.mjs
configuration file. - Adds Tailwind to the
integrations
array inastro.config.mjs
.
Example Tailwind Usage
<body>
<Header />
<main class="ml-2">
<!-- Your content -->
</main>
<Footer />
</body>
For details, visit the Tailwind Integration Guide.
IDE Support
Astro offers IDE plugins to enhance the development experience:
IDE | Provided By | Stability |
---|---|---|
VS Code | Astro | Stable |
JetBrains | JetBrains | Unstable impression as of Nov 2023 |
Leverage these plugins for syntax highlighting, IntelliSense, and error detection.
Conclusion
Astro’s emphasis on performance, simplicity, and developer-friendly tools ensures an enjoyable and efficient experience for building static websites. The combination of features like Astro Islands and Zero JS by default makes it a trendsetter among modern static site generators.
I hope this post inspires you to explore Astro for your next project.
Happy Coding! 🚀