1. Setting Up a New Astro Project
Getting an Astro project up and running is simple with the official command-line interface (CLI). First, make sure you have Node.js (version 18.14.1 or higher) installed. Then, open your terminal and run the following command:
# create a new project with npm
npm create astro@latest
This command kicks off a setup wizard that will guide you through a few questions:
- Where should we create your new project?
You can type
.to use the current directory or provide a new folder name (e.g.,./my-astro-site). - Which template would you like to use? For this tutorial, select the "Empty" template to start from scratch.
- Would you like to install dependencies?
Press
yto let Astro install the necessary packages. - Would you like to initialize a new git repository?
Press
yif you plan to use Git for version control.
TypeScript Support by Default
Astro is a JavaScript framework that comes with first-class TypeScript support out of the box. During the setup wizard, you'll be asked to choose a TypeScript strictness level (Strict, Strictest, or Relaxed). You can start writing TypeScript in your
.astro
and
.ts
files immediately with no extra configuration. Of course, if you prefer, you can write plain JavaScript instead—Astro supports both seamlessly.
Using Command-Line Flags
For automated setups, you can bypass the interactive wizard by providing flags directly in the command line. If using npm, you must add
--
before the flags.
--template <name>: Specifies a starter template (e.g.,minimal,blog).--installor--no-install: Skips or forces dependency installation.--gitor--no-git: Skips or forces Git repository initialization.--skip-houston: Skips the wizard entirely.
For example, to create a new project named
my-silent-app
using the minimal template and skipping installation, you would run:
npm create astro@latest my-silent-app -- --template minimal --skip-houston --no-install
Once the setup is complete, navigate into your new project directory and start the development server:
cd your-project-name
npm run dev
You can now view your new site running live at http://localhost:4321.
2. How to Add a Page
Astro uses a file-based routing system, which means every
.astro,
.md, or
.html
file you create in the
src/pages/
directory automatically becomes a page on your website. Let's create an "About" page:
- Create a new file inside the
src/pages/directory namedabout.astro. - Add the following content to your new file:
---
// src/pages/about.astro
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>About Me</title>
</head>
<body>
<h1>About Me</h1>
<p>This is the about page for my new Astro site!</p>
<a href="/">Go back home</a>
</body>
</html>
Now, if you visit
http://localhost:4321/about
in your browser, you will see your new page. It's that simple!
3. How to Add a Component
Components are reusable pieces of UI that you can use across multiple pages. It's a best practice to keep them in the
src/components/
directory. Let's create a reusable
Header
component:
- Create a new directory named
src/components/. - Inside
src/components/, create a new file namedHeader.astro. - Add the following code to
Header.astro:
---
// src/components/Header.astro
export interface Props {
title: string;
}
const { title } = Astro.props;
---
<header>
<h1>{title}</h1>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
<style>
header {
background: #f7f7f7;
padding: 1rem;
border-bottom: 1px solid #eee;
}
nav a {
margin-right: 1rem;
}
</style>
Now, you can import and use this component in any of your pages. Let's update
index.astro:
---
// src/pages/index.astro
import Header from '../components/Header.astro';
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>My Astro Site</title>
</head>
<body>
<Header title="Welcome to my Astro Site!" />
<main>
<p>This is the homepage.</p>
</main>
</html>
4. How to Create Dynamic Routes
Dynamic routing allows you to generate pages programmatically from a single file. This is perfect for blog posts or product pages. Let's create dynamic pages for a series of blog posts.
- Create a file at
src/pages/posts/[id].astro. The square brackets[id]signify a dynamic parameter. - Add the following code. This file is responsible for fetching the data and defining which routes to generate.
---
// src/pages/posts/[id].astro
import Header from '../../components/Header.astro';
export async function getStaticPaths() {
const posts = [
{ id: '1', title: 'My First Post', content: 'This is the content of my very first post.' },
{ id: '2', title: 'Astro is Awesome', content: 'I am really enjoying learning Astro.' },
{ id: '3', title: 'Building with Components', content: 'Components make everything so much easier.' }
];
return posts.map(post => ({
params: { id: post.id },
props: { post },
}));
}
const { post } = Astro.props;
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>{post.title}</title>
</head>
<body>
<Header title="My Blog" />
<main style="padding: 1rem;">
<h2>{post.title}</h2>
<p>{post.content}</p>
</main>
</body>
</html>
With this single file, Astro will generate three pages:
/posts/1,
/posts/2, and
/posts/3. The
getStaticPaths
function returns an array of objects, where each object defines the
params
(the URL) and
props
(the data) for a specific page.
Linking to External Pages
It's important to note that dynamic routing with
getStaticPaths
is specifically for
generating pages on your own site. It cannot be used to create redirects or links that point to external websites.
To create a dynamic list of external links, you would use a standard Astro page. In the frontmatter, you can fetch or define your list of links, and then use the
.map()
function in the template to render them as standard
<a>
tags.
For example, to create a page listing your social media profiles:
---
// src/pages/socials.astro
const socialProfiles = [
{ name: 'GitHub', url: 'https://github.com/withastro' },
{ name: 'Twitter', url: 'https://twitter.com/astrodotbuild' },
{ name: 'Discord', url: 'https://astro.build/chat' }
];
---
<html>
<head><title>Our Socials</title></head>
<body>
<h1>Follow us!</h1>
<ul>
{socialProfiles.map(profile => (
<li>
<a href={profile.url} target="_blank" rel="noopener noreferrer">
{profile.name}
</a>
</li>
))}
</ul>
</body>
</html>
5. How to Use Plugins (Integrations)
Astro has a powerful system for adding new features and integrations with other tools. In Astro, these are called Integrations. You can add features like React components, Tailwind CSS, and sitemaps.
You can add an official integration automatically with the
astro add
command. For example, to add the React integration, you would run:
npx astro add react
This command will automatically install the necessary packages and update your
astro.config.mjs
file.
Performance Impact
A key benefit of Astro's architecture is that most integrations are
build-time only. This means they do their work during development and add
zero client-side JavaScript
to your published site. For example, integrations for Tailwind, sitemaps, or MDX processing won't slow down your users. Integrations for UI frameworks like React or Vue are special; they only add JavaScript to the page if you specifically mark a component as interactive using a
client:*
directive, giving you full control over your site's performance.
6. How to Add Tailwind CSS
Tailwind CSS is a first-class citizen in the Astro ecosystem and provides a fantastic developer experience for styling your site. You can add it using the Astro Tailwind integration:
npx astro add tailwind
This command will install Tailwind, create a
tailwind.config.mjs
file, and add the integration to your
astro.config.mjs. You can then start using Tailwind's utility classes throughout your project.
For beautiful default typography styles for long-form content (like blog posts or this tutorial), you can use the official
Tailwind CSS Typography
plugin. It provides the
prose
classes which style your text automatically.
7. How to Add Markdown Parser Plugins
Astro's Markdown processing is highly extensible. While the MDX integration is popular for embedding components, you can also add plugins to support new syntax (like tables or footnotes) or to modify the final HTML output.
This is done using remark and rehype plugins.
- Remark plugins
transform the Markdown itself (e.g., adding support for GitHub Flavored Markdown tables with
remark-gfm). - Rehype plugins
manipulate the final HTML output (e.g., automatically adding
ids to headings for anchor links).
You can add these plugins in your
astro.config.mjs
file. First, install the desired plugin, then import it and add it to the configuration:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import remarkGfm from 'remark-gfm';
export default defineConfig({
markdown: {
remarkPlugins: [remarkGfm],
// rehypePlugins: [],
},
});
You can find extensive lists of available plugins to explore:
8. Organizing Content with Collections
For content-heavy sites, Astro provides a powerful feature called Content Collections. This is the best way to manage related content, like blog posts, by enforcing a common configuration and providing full TypeScript support.
Instead of placing posts in
src/pages/, you place them in a dedicated
src/content/
directory. You then define a schema that validates the front matter of each entry.
- Create a file at
src/content/config.tsand define a schema for your blog posts. This ensures every post has a title, description, and publish date.
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const blogCollection = defineCollection({
schema: z.object({
title: z.string(),
description: z.string(),
publishDate: z.date(),
}),
});
export const collections = {
'blog': blogCollection,
};
- Move your Markdown posts into
src/content/blog/. Now, Astro will validate each post against your schema. - Use the
getCollection()API on a page (likesrc/pages/blog.astro) to query all your posts and render a list.
---
// src/pages/blog.astro
import { getCollection } from 'astro:content';
const allBlogPosts = await getCollection('blog');
---
<html>
<body>
<h1>My Blog</h1>
<ul>
{allBlogPosts.map(post => (
<li><a href={`/blog/${post.slug}/`}>{post.data.title}</a></li>
))}
</ul>
</body>
</html>
This approach provides a robust, type-safe way to manage and query your content, making it the recommended method for any project with multiple pieces of similar content.
9. Integrating with Storybook.js
Storybook is a powerful tool for developing UI components in isolation. Astro provides an official integration to make setting it up seamless.
- Add the integration: Run the following command in your project's terminal:
npx astro add storybook
This command will install all necessary dependencies, create configuration files in a new
.storybook/
directory, and add the required scripts to your
package.json.
- Create a story:
A "story" is a file that demonstrates a single state of a component. For a component located at
src/components/Button.astro, you would create a story atsrc/components/Button.stories.js.
// src/components/Button.stories.js
import Button from './Button.astro';
export default {
title: 'Components/Button',
};
export const Primary = () => <Button label="Primary Button" />;
export const Secondary = () => <Button label="Secondary Button" variant="secondary" />;
- Run Storybook: Start the Storybook development server to view and interact with your components.
npm run storybook
10. How to Add PWA and Service Worker Support
You can enhance your Astro site to work offline and make it installable like a native app by turning it into a Progressive Web App (PWA). This is handled by a
service worker. While you could write a service worker script manually, the recommended approach is to use the
@vite-pwa/astro
integration.
- Add the integration: Run the following command in your project's terminal:
npm install -D @vite-pwa/astro
- Configure it: After installation, you need to add and configure the integration in your
astro.config.mjs
file. The integration uses Google's Workbox library under the hood, giving you powerful control over caching with sensible defaults.
Handling the Web Manifest
The
@vite-pwa/astro
integration also handles the creation and injection of the
web manifest for you. Typically, you create a base
manifest.json
file in your project where you define your app's name, icons, and colors. The integration then processes this file during the build and automatically adds the correct
<link rel="manifest" ...>
tag into the
<head>
of your pages. This automates a key part of the PWA setup.
11. Where to Find Information About Available Plugins
The Astro team and community maintain a large collection of official and community-built integrations. You can explore all the available options in the official integrations directory.
Explore all Astro Integrations →
This is the best place to find integrations for:
- UI Frameworks (React, Vue, Svelte)
- CSS & Styling (Tailwind, Sass)
- Adapters for deploying to different hosts (Vercel, Netlify)
- Site utilities (Sitemaps, RSS feeds)
- And much more!