When blogging, I would rather spend my time on the content: write posts in Markdown, organize pages with a few components, and publish the site. Astro fits this workflow well, and astro-aria provides a personal website template that you can make your own.
This article starts with Astro’s basic concepts and usage, then introduces the structure and customization options of astro-aria.
Sources checked on October 24, 2026. The current stable Astro release is 7.4.2. The framework examples below follow the current official documentation; the upstream astro-aria template still declares Astro 4, so keep the version differences in mind when using the theme.
What Is Astro?
Astro is a web framework for content websites, including blogs, documentation, personal homepages, and portfolios. One of its core ideas is to render content as HTML first, then add JavaScript only to the parts that actually need interaction.
For example, a post’s title, body, and footer usually need only HTML and CSS. A comment box, search field, or interactive chart needs code running in the browser. Astro lets you handle these parts separately without turning the entire page into a client application. This approach is called islands architecture.
Astro’s own .astro components do not ship a client runtime by default. Integrations also let you use UI frameworks such as React, Vue, and Svelte. For a blog focused on content, you can start with .astro and Markdown, then bring in additional tools when you need interaction. See the component documentation.
Create and Run Your First Project
According to the current official installation guide, you need Node.js 22.23.4 or a newer supported version. Odd-numbered major versions of Node.js are not supported. Check your environment, then run the setup wizard:
node --version
npm create astro@latest
Suppose you name the directory my-astro-blog in the wizard and choose to install dependencies:
cd my-astro-blog
npm run dev
Open the local address printed in your terminal to preview the site. If you skipped dependency installation during setup, run npm install first.
Here are a few common commands. For details, see the Astro CLI reference:
| Command | Purpose |
|---|---|
npm run dev |
Start the development server and preview changes as you edit |
npm run build |
Build the deployment output |
npm run preview |
Preview the build locally |
npx astro add react |
Install the React integration for a project that needs it |
preview is mainly for checking the build locally. To make the site publicly available, deploy the output to your hosting platform.
Understand the Project Structure
Directories vary slightly between templates. The following is a common layout; folders such as layouts, components, and the content directory are conventions you can organize yourself:
my-astro-blog/
├── public/ # Static assets copied without processing
├── src/
│ ├── components/ # Reusable components
│ ├── layouts/ # Page layouts
│ ├── pages/ # Pages and routes
│ ├── content/blog/ # Example post directory
│ └── content.config.ts # Content collection configuration
├── astro.config.mjs # Astro configuration
└── package.json # Dependencies and scripts
src/pages determines the page routes. For example, src/pages/index.astro is the homepage, and src/pages/about.astro maps to /about. Markdown stored in other content directories needs a page to query and render it before it has a public URL. Creating a content directory does not automatically generate routes. See the project structure guide and routing documentation.
Write an Astro Page
Add the following to src/pages/about.astro:
---
const name = "a developer who enjoys documenting things";
const description = "I share programming experiences, personal projects, and notes on life here.";
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>About Me</title>
</head>
<body>
<h1>Hello, I'm {name}</h1>
<p>{description}</p>
</body>
</html>
<style>
h1 {
color: #6d28d9;
}
</style>
The section between the two --- markers at the top is the component script, followed by the template and styles. The script can import components, read data, and use await. It runs during the build or server rendering and is not sent to visitors as a browser script. For browser interaction, you can write a <script> or use a UI framework component. See Astro component syntax.
When several pages share a header, navigation, and footer, move those parts into a layout component and use <slot /> to receive each page’s content.
Load Interaction on Demand: Client Directives
Suppose you have installed the React integration and created src/components/Counter.jsx. You can use it in an Astro page like this:
---
import Counter from "../components/Counter.jsx";
---
<Counter client:visible />
client:visible loads and activates the component’s interaction when it enters the viewport. You can choose other strategies depending on when the component needs to become interactive:
| Directive | Use case |
|---|---|
client:load |
Components that need interaction as soon as the page loads |
client:idle |
Components that can wait until the browser is idle |
client:visible |
Components that need interaction only after scrolling into view |
client:media="(max-width: 50em)" |
Components that need activation only when a media query matches |
These directives apply to UI framework components imported directly into a .astro file. Without a client:* directive, a framework component renders HTML by default and does not automatically gain client interaction. For restrictions and other directives, see the official directive reference.
Manage Posts with Markdown and Content Collections
As your post count grows, content collections give you a consistent way to load files, validate frontmatter, and get type hints. The current documentation places build-time content collection configuration in src/content.config.ts.
The following example is for a separate new project. It uses a blog collection and does not require replacing astro-aria’s existing post configuration:
// src/content.config.ts
import { defineCollection } from "astro:content";
import { glob } from "astro/loaders";
import { z } from "astro/zod";
const blog = defineCollection({
loader: glob({
base: "./src/content/blog",
pattern: "**/*.md",
}),
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
}),
});
export const collections = { blog };
Next, create src/content/blog/hello-astro.md:
---
title: My First Astro Post
description: Start documenting my learning journey with Markdown.
pubDate: 2026-10-24
---
## Why Start a Blog?
Document how you solve a problem so you have something to refer to next time.
The frontmatter here contains post metadata. Its purpose differs from the JavaScript component script in a .astro file.
Use await getCollection("blog") to retrieve a list and access each entry’s metadata through data. If you need posts sorted by date, sort them explicitly rather than relying on the returned order. Render the body with the standalone render(entry) function. For example, create src/pages/blog/[...id].astro:
---
import { getCollection, render } from "astro:content";
/** Takes no arguments; returns static route parameters and posts for the blog collection. */
export async function getStaticPaths() {
const posts = await getCollection("blog");
return posts.map((post) => ({
params: { id: post.id },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await render(post);
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{post.data.title}</title>
</head>
<body>
<h1>{post.data.title}</h1>
<Content />
</body>
</html>
After building, the example post is available at /blog/hello-astro. The current content layer uses an entry’s id and render(entry). Do not copy entry.slug or entry.render() directly from older tutorials. For a complete explanation of loading, validation, and rendering, see the content collection documentation.
Build and Deploy
Astro uses static output by default, so blog content is usually generated at build time. Once you have finished writing, run:
npm run build
npm run preview
For a default static project, the build output is in dist/. Hosting platforms typically ask for a build command and a publish directory. If your repository puts the project in an app/ subdirectory, also set the project root to app. Follow the official deployment guide for your platform’s configuration.
If a page needs to read user data or execute server logic on every request, install an adapter for your deployment platform and configure on-demand rendering. A typical Markdown blog can start with the default static mode. See the on-demand rendering documentation.
astro-aria: A Blog and Portfolio in One Place
astro-aria is a personal website template that ports the Aria template to Astro. It brings a personal introduction, project showcase, and posts into one set of pages, making it useful for developers who want to share both their work and their writing.
The homepage source and navigation component show a personal introduction section, projects, posts, a mobile menu, and a light/dark mode toggle. The design uses generous whitespace, rounded corners, and dashed borders, with a focused reading area.
It works as a website template whose source you can edit directly. Start by changing the homepage copy, portrait, project data, and menu, then adjust the layout as needed.
Install the Theme
Using the Astro setup tool’s GitHub template support, you can specify the repository covered in this article directly:
npm create astro@latest -- --template miantiao-me/astro-aria
Follow the wizard to create the project and install dependencies, then enter the generated directory and run npm run dev.
The theme’s README still refers to the old repository name, ccbikai/astro-aria. This article uses miantiao-me/astro-aria. Also keep in mind that create astro@latest runs the latest setup tool; it does not mean the template’s dependencies are automatically upgraded to their latest versions.
As of the date checked, the upstream package.json still declares astro: ^4.8.2, Tailwind CSS 3, and @astrojs/tailwind. The local copy of this project declares Astro ^7.4.2 in app/package.json and uses the Tailwind CSS 4 Vite plugin. Base your changes on the versions actually installed. For upgrades, see the official upgrade guide.
Find the Files to Customize
The following paths are relative to the theme root. In this repository, add the app/ prefix:
| What to change | Location |
|---|---|
| Homepage introduction and portrait reference | src/pages/index.astro |
| About page | src/pages/about.astro |
| Navigation labels and links | src/collections/menu.json |
| Project data | src/collections/projects.json |
| Experience data | src/collections/experiences.json |
| Logo and footer | src/components/logo.astro, src/components/footer.astro |
| Shared page structure | src/layouts/main.astro |
| Post typography and layout | src/layouts/post.astro |
| Static assets such as portraits and icons | public/assets/images/ |
You can find these locations in the theme’s source directory. When publishing a Chinese site, also check the shared layout’s lang attribute, page titles, navigation, and light/dark mode button labels so the page language matches its content.
Write a Post in the Theme
The theme uses a collection named post and requires three fields: title, description, and dateFormatted. See the upstream collection configuration. Following the theme’s existing conventions, create src/content/post/hello-astro.md:
---
layout: ../../layouts/post.astro
title: Building My Personal Blog with Astro
description: Documenting the journey from choosing a theme to publishing my first post.
dateFormatted: Oct 24, 2026
---
## Start Writing
This is my first post. I will continue sharing what I learn and build.
Here, dateFormatted is used both for display and for sorting posts. The theme’s post list component parses it as an English month, day, and year, so keep a format such as Oct 24, 2026. If you want to display dates in Chinese, update the date handling logic as well.
The upstream theme still uses the older content API. The local project already loads src/content/post with glob in src/content.config.ts and uses entry.id and render(entry) in the post route. Follow the local format when writing; there is no need to switch back to the older configuration.
This repository also has a separate src/content/zh/ directory for Chinese posts, where the Chinese original of this article is stored. However, the existing loader reads only src/content/post, and the post list and detail route query only the post collection. As a result, posts under zh do not currently appear on the website automatically. This English translation is stored in src/content/post, which is already included in the collection. Publishing by language would require configuring the corresponding collections, lists, and routes; a directory name alone does not enable multilingual support.
Start with Your First Post
If you want to understand how the site is built, use the official wizard to create a small project and practice with pages, components, and content collections. If you already know you need a personal homepage and blog, astro-aria provides a convenient starting point to customize.
For a personal blog, start by completing the homepage introduction, replacing the personal details, writing your first post, and checking the deployment. As your content grows, add categories, search, or other features when you actually need them.
My Project: PawfectPix
Alongside documenting what I learn and build, I also work on my own products. PawfectPix is my online AI pet portrait generator: upload a pet photo, choose an art style, and turn an everyday snapshot into a portrait with personality.
It offers styles ranging from watercolor and vintage looks to cinematic scenes, with watermark-free downloads at up to 4K. The same photo can also produce a playful personality roast of your pet. Whether you want a new avatar, something to share with friends, or a printable keepsake, you can start with a single photo.
Try it here: PawfectPix — AI Pet Portrait Generator.
Further reading: Astro documentation, Astro 7.4 release notes, and the astro-aria repository.