Astro / 9 MIN READ
Starting an Astro Project
Starting an Astro Project
From the original Fervor library. Examples may use older package versions.
Complete Guide to Starting an Astro Project
This comprehensive tutorial will walk you through setting up and developing a website with Astro from scratch.
Table of Contents
- Prerequisites
- Creating a New Astro Project
- Understanding the Project Structure
- Creating Pages
- Creating Layouts
- Creating Components
- Styling with CSS
- Adding Tailwind CSS
- Working with Images
- Adding Interactivity
- Data Fetching
- Building and Deployment
- Common Issues and Troubleshooting
Prerequisites
Before starting, ensure you have:
- Node.js: v16.12.0 or higher installed
- npm or yarn or pnpm package manager
- A code editor (VS Code recommended)
- Basic familiarity with HTML, CSS, and JavaScript
To check your Node.js version:
node -v
Creating a New Astro Project
-
Open your terminal and navigate to where you want to create your project.
-
Run the create command:
npm create astro@latest
-
Follow the prompts:
- Enter your project name (e.g.,
my-astro-site) - Choose a template:
- For beginners, select “Empty” or “Blog” for a more structured start
- Choose whether to install dependencies automatically
- Choose whether to initialize a git repository
- Choose your preferred package manager (npm, yarn, or pnpm)
- Enter your project name (e.g.,
-
Navigate to your new project directory:
cd my-astro-site
- Start the development server:
npm run dev
- Open your browser to
http://localhost:4321to see your site.
Understanding the Project Structure
Here’s what you’ll find in your new Astro project:
my-astro-site/
├── public/ # Static assets like images, fonts, etc.
├── src/
│ ├── components/ # Reusable UI components
│ ├── layouts/ # Layout components used by pages
│ └── pages/ # Each file becomes a route in your site
├── astro.config.mjs # Astro configuration file
├── package.json # Project dependencies and scripts
└── tsconfig.json # TypeScript configuration (if using TS)
Key directories and files explained:
- src/pages/: Every
.astro,.md, or.mdxfile here becomes a page on your site - src/components/: Place reusable components here
- src/layouts/: For page layouts and templates
- public/: Static files that don’t need processing
- astro.config.mjs: Configure Astro and integrations
Creating Pages
In Astro, every file in the src/pages/ directory becomes a route.
- Create a home page (if not already present):
---
// src/pages/index.astro
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>My Astro Site</title>
</head>
<body>
<h1>Welcome to my Astro site!</h1>
</body>
</html>
- Create an about page:
---
// src/pages/about.astro
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>About - My Astro Site</title>
</head>
<body>
<h1>About Me</h1>
<p>This is the about page of my Astro website.</p>
<a href="/">Go home</a>
</body>
</html>
Navigate to http://localhost:4321/about to view your about page.
Creating Layouts
Layouts help you avoid repeating code across pages:
- Create a main layout:
---
// src/layouts/MainLayout.astro
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>{Astro.props.title || 'My Astro Site'}</title>
</head>
<body>
<nav>
<div class="nav-links">
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/blog">Blog</a>
</div>
</nav>
<main>
<slot />
</main>
<footer>
<p>© {new Date().getFullYear()} My Astro Site</p>
</footer>
</body>
</html>
<style>
nav {
background: #f1f1f1;
padding: 1rem;
}
.nav-links {
display: flex;
gap: 1rem;
}
main {
padding: 2rem;
max-width: 80ch;
margin: 0 auto;
}
footer {
text-align: center;
padding: 1rem;
background: #f1f1f1;
}
</style>
- Update your home page to use the layout:
---
// src/pages/index.astro
import MainLayout from '../layouts/MainLayout.astro';
---
<MainLayout title="Home - My Astro Site">
<h1>Welcome to my Astro site!</h1>
<p>This is the homepage of my Astro website.</p>
</MainLayout>
- Update your about page similarly:
---
// src/pages/about.astro
import MainLayout from '../layouts/MainLayout.astro';
---
<MainLayout title="About - My Astro Site">
<h1>About Me</h1>
<p>This is the about page of my Astro website.</p>
</MainLayout>
Creating Components
Components help you organize and reuse UI elements:
- Create a button component:
---
// src/components/Button.astro
const { text, href } = Astro.props;
---
<a href={href} class="button">
{text}
</a>
<style>
.button {
display: inline-block;
padding: 0.5rem 1rem;
background-color: #4c1d95;
color: white;
text-decoration: none;
border-radius: 0.25rem;
transition: background-color 0.2s;
}
.button:hover {
background-color: #6d28d9;
}
</style>
- Use the button in your homepage:
---
// src/pages/index.astro
import MainLayout from '../layouts/MainLayout.astro';
import Button from '../components/Button.astro';
---
<MainLayout title="Home - My Astro Site">
<h1>Welcome to my Astro site!</h1>
<p>This is the homepage of my Astro website.</p>
<Button text="Learn More" href="/about" />
</MainLayout>
Styling with CSS
Astro supports various ways to style your components:
1. Scoped styles (component-level)
---
// Any component or page
---
<div class="container">
<h1>Hello World</h1>
</div>
<style>
/* These styles only apply to this component */
.container {
max-width: 80ch;
margin: 0 auto;
padding: 2rem;
}
h1 {
color: navy;
}
</style>
2. Global styles
- Create a global CSS file:
/* src/styles/global.css */
:root {
--font-size-base: 1rem;
--color-primary: #4c1d95;
--color-text: #444444;
}
* {
box-sizing: border-box;
margin: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-size: var(--font-size-base);
line-height: 1.6;
color: var(--color-text);
}
h1 {
margin: 1rem 0;
font-size: 2.25rem;
}
- Import it in your layout:
---
// src/layouts/MainLayout.astro
import '../styles/global.css';
---
Adding Tailwind CSS
- Install Tailwind CSS and its dependencies:
npm install -D tailwindcss @astrojs/tailwind
- Add the Tailwind integration to your Astro config:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
export default defineConfig({
integrations: [tailwind()]
});
- Create a Tailwind config file (will be automatically created, but you can customize it):
// tailwind.config.mjs
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {},
},
plugins: [],
}
- Create a global CSS file with Tailwind directives:
/* src/styles/global.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
- Import it in your layout:
---
// src/layouts/MainLayout.astro
import '../styles/global.css';
---
- Use Tailwind classes in your components:
---
// src/pages/index.astro
import MainLayout from '../layouts/MainLayout.astro';
---
<MainLayout title="Home - My Astro Site">
<div class="max-w-4xl mx-auto p-4">
<h1 class="text-4xl font-bold text-purple-900 mb-4">
Welcome to my Astro site!
</h1>
<p class="text-gray-700 mb-6">
This is the homepage styled with Tailwind CSS.
</p>
<a href="/about" class="bg-purple-700 hover:bg-purple-800 text-white font-medium py-2 px-4 rounded transition-colors">
Learn More
</a>
</div>
</MainLayout>
Working with Images
- Place static images in the
publicdirectory:
my-astro-site/
├── public/
│ └── images/
│ └── profile.jpg
- Reference them in your components:
<img src="/images/profile.jpg" alt="Profile picture" />
-
For optimized images, install the
astro:assetsintegration (included by default in newer Astro projects). -
Use optimized images:
---
// src/pages/about.astro
import MainLayout from '../layouts/MainLayout.astro';
import { Image } from 'astro:assets';
import profileImage from '../assets/profile.jpg';
---
<MainLayout title="About - My Astro Site">
<h1>About Me</h1>
<Image src={profileImage} alt="Profile picture" width={400} height={400} />
<p>This is the about page of my Astro website.</p>
</MainLayout>
Adding Interactivity
Astro supports client-side interactivity through “islands” of reactivity:
1. Using client-side scripts:
---
// Any component
---
<button id="counter">Clicks: 0</button>
<script>
// This JavaScript runs in the client
const button = document.getElementById('counter');
let count = 0;
button.addEventListener('click', () => {
count++;
button.textContent = `Clicks: ${count}`;
});
</script>
2. Using UI frameworks (React, Vue, Svelte, etc.):
- Install a framework integration, for example React:
npm install react react-dom @astrojs/react
- Add it to your Astro config:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
import react from '@astrojs/react';
export default defineConfig({
integrations: [tailwind(), react()]
});
- Create a React component:
// src/components/Counter.jsx
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onClick={() => setCount(count + 1)}
>
Clicks: {count}
</button>
);
}
- Use it in an Astro component:
---
// src/pages/index.astro
import MainLayout from '../layouts/MainLayout.astro';
import Counter from '../components/Counter.jsx';
---
<MainLayout title="Home - My Astro Site">
<h1>Welcome to my Astro site!</h1>
<p>This is an interactive counter:</p>
<Counter client:load />
</MainLayout>
The client:load directive tells Astro to hydrate the component on page load.
Data Fetching
Astro components can fetch data during build time (by default) or at runtime (with SSR enabled):
1. Static data fetching:
---
// src/pages/blog.astro
import MainLayout from '../layouts/MainLayout.astro';
// This runs at build time
const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5');
const posts = await response.json();
---
<MainLayout title="Blog - My Astro Site">
<h1>Blog Posts</h1>
<ul>
{posts.map(post => (
<li>
<h2>{post.title}</h2>
<p>{post.body}</p>
</li>
))}
</ul>
</MainLayout>
2. Server-side rendering (optional):
- Enable SSR in your config:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
import react from '@astrojs/react';
// Choose an adapter for your deployment platform
import node from '@astrojs/node';
export default defineConfig({
integrations: [tailwind(), react()],
output: 'server',
adapter: node({
mode: 'standalone'
})
});
- Use dynamic routes and server-side data fetching:
---
// src/pages/posts/[id].astro
import MainLayout from '../../layouts/MainLayout.astro';
export async function getStaticPaths() {
const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5');
const posts = await response.json();
return posts.map(post => ({
params: { id: post.id.toString() },
props: { post },
}));
}
const { post } = Astro.props;
---
<MainLayout title={post.title}>
<h1>{post.title}</h1>
<p>{post.body}</p>
<a href="/blog">Back to Blog</a>
</MainLayout>
Building and Deployment
- Build your site for production:
npm run build
This creates a dist/ directory with your built site.
- Preview your built site locally:
npm run preview
- Deploy your site:
Astro sites can be deployed to various platforms:
- Netlify: Connect your GitHub repository and set the build command to
astro build - Vercel: Similar to Netlify, connect your repository and Vercel will detect Astro automatically
- GitHub Pages: Use an action to build and deploy to GitHub Pages
- Any static hosting: Upload the contents of your
dist/folder
For SSR deployments, you’ll need to use a Node.js hosting platform or a serverless environment.
Common Issues and Troubleshooting
Issue: Tailwind styles not working
Solution: Make sure your content paths in tailwind.config.mjs are correct and that you’ve imported the CSS file with Tailwind directives.
Issue: Node.js version errors
Solution: Ensure you’re using Node.js 16.12.0 or higher. Use a tool like nvm to manage Node.js versions.
Issue: “Could not determine executable to run”
Solution: This may indicate path issues with npm. Try:
- Reinstalling npm:
npm install -g npm - Running commands with administrator privileges
- Using an alternative package manager like yarn or pnpm
Issue: Changes not reflected in the browser
Solution:
- Make sure your development server is running
- Try hard-refreshing your browser (Ctrl+F5 or Cmd+Shift+R)
- Check for errors in the terminal or browser console
Issue: Images not loading
Solution:
- Check that the path is correct relative to the
publicdirectory - For optimized images, make sure you’re importing them correctly
Issue: Framework components not hydrating
Solution:
- Ensure you’ve added the framework integration to your
astro.config.mjs - Make sure you’re using a client directive (e.g.,
client:load)
Next Steps
Now that you have your Astro project set up, you can:
- Add content collections for structured content
- Explore more integrations like MDX for enhanced Markdown
- Add a CMS like Contentful or Sanity
- Set up a contact form with form handling
- Add animations and transitions for a polished feel
Congratulations! You now have a solid foundation for building fast, modern websites with Astro.