What Is Astro? An Introduction to the Popular Static Site Generator
Tags: astro, webdev, programming, tutorial
Published: 2023-10-11 13:00:00

Web development has evolved significantly from static single-page websites to offering a variety of languages, frameworks, and systems for every niche.

Now we have Astro, a new and easy-to-use tool by Fred K. Schott and a group of other contributors. Developers love it because it’s simple like old-school web pages but powerful.

We'll explain why it's popular and how you can use Astro to easily make a blog!

## **What Is Astro?**

[Astro](https://kinsta.com/blog/svelte-static-site-generator/#2-astro), or Astro.js, is a popular [static site generator](https://kinsta.com/blog/static-site-generator/) conceived for those who want to create content-rich websites that run quickly and smoothly. Its lightweight nature, intuitive structure, and gentle learning curve make it attractive to developers of all experience levels.

Despite its small footprint, Astro comes with powerful tools that drastically increase your site’s flexibility, saving you hours in content and theme management. In addition, it gives developers the option of working with their preferred frameworks in conjunction with Astro — an appealing prospect for seasoned coders who already have a host of favorites.

Here are just a few of the ways Astro stands out from the crowd:

* **Island architecture:** Astro extracts your user interface (UI) into smaller, isolated components known as “Astro Islands” that can be used on any page. Unused JavaScript is replaced with lightweight HTML.
    
* **Zero JavaScript (by default):** While you can use all the JavaScript you want to create your websites, Astro will attempt to deploy zero JavaScript to production by transcribing your code for you. This a perfect approach if your focus is on site speed.
    
* **SSG and SSR included:** Astro started as a static site generator, but along the way, it became a framework that uses both static site generation (SSG) and [server-side rendering (SSR)](https://kinsta.com/blog/web-application-architecture/#serversiderendered-architecture). And you can pick which pages will use which approach.
    
* **Framework-agnostic:** When using Astro, you can use any [JavaScript framework](https://kinsta.com/blog/javascript-libraries/) you like — even multiple frameworks at once. (We’ll discuss this in greater detail later in this article.)
    

What’s more, Astro is edge-ready, meaning it can be deployed anywhere, anytime, with ease.

## **Astro’s Structure**

Before we venture any further, it’s important to understand how Astro is set up so you can use it effectively. Let’s take a look at Astro’s core file structure:

```plaintext
├── dist/
├── src/
│   ├── components/
│   ├── layouts/
│   └── pages/
│       └── index.astro
├── public/
└── package.json
```

As you can see, the structure itself is quite simple. However, there are some key points you should remember:

* Most of our project lives in the **src** folder. You can arrange your components, layouts, and pages into subfolders. You may add additional folders to make your project easier to navigate.
    
* The **public** folder is for all the files that live outside of the build process, such as fonts, images, or a **robots.txt** file.
    
* The **dist** folder will contain all the content you want to deploy on your production server.
    

Next, let’s dive deeper into Astro’s main constituents: components, layouts, and pages.

### **Components**

Components are reusable chunks of code that can be included all over your website, similar to [shortcodes in WordPress](https://kinsta.com/blog/wordpress-shortcodes/). By default, they have the file extension **.astro**, but you can also use non-Astro components built with [Vue](https://kinsta.com/blog/vue-js/), [React](https://kinsta.com/blog/react-static-site-generator/#4-astro), Preact, or [Svelte](https://kinsta.com/blog/svelte-static-site-generator/#2-astro).

The following is an example of what a simple component looks like — in this case, a classed `div` tag containing an `h2`:

```html
<!-- src/components/Kinsta.astro -->
<div class="kinsta_component">
    <h2>Hello, Kinsta!</h2>
</div>
```

And here’s how we can incorporate that component into our site:

```jsx
---
import KinstaComponent from ../components/Kinsta.astro
---
<div>
    <KinstaComponent />
</div>
```

As demonstrated above, you first have to import the component. Only then can it be included on the page.

Now it’s time to add some properties to our component. Let’s start with a `{title}` property:

```jsx
---
const { title = 'Hello' } = Astro.props
---

<div class="kinsta_component">
    <h2>{title}</h2>
</div>
```

And here’s how our property would be implemented:

```jsx
---
import KinstaComponent from ../components/Kinsta.astro
---

<div>
    <!-- This shows "Good day" -->
    <KinstaComponent title="Good day"/>

    <!-- This shows "Hello" -->
    <KinstaComponent />
 </div>
```

Simple, right?

As you’ve probably already realized, the real power of Astro’s components is in their global and reusable nature. They enable you to make sweeping changes to your entire site by editing only a few lines of code, which can save you countless hours that would otherwise be spent on tedious, painstaking text replacements.

### **Layouts**

Now, let’s talk about layouts. In addition to their familiar thematic function, layouts in Astro are also reusable components, but they’re employed as code wrappers.

Take a look at this example:

```jsx
---
// src/layouts/Base.astro
const { pageTitle = 'Hello world' } = Astro.props
---

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <title>{pageTitle}</title>
</head>
<body>
    <main>
        <slot />
    </main>
</body>
</html>
```

Note the `<slot />` tag here. The `<slot />` element in Astro acts as a placeholder for actual HTML tags and content.

Let’s see it in action.

The code below shows our `<slot />` tag getting replaced with our desired code, all of which is wrapped by our **Base.astro** layout:

```jsx
---
import Base from '../layouts/Base.astro';
---

<Base title="Hello world">
    <div>
        <p>Some example text.</p>
    </div>
</Base>
```

As you can see, our `<slot />` tag was replaced by the HTML it represents, which is:

```html
<div>
    <p>Some example text.</p>
</div>
```

As you can see, layouts, like components, allow you to reuse chunks of code across your site, simplifying the challenge of updating your global content and design.

### **Pages**

Pages are a special type of component that is responsible for routing, data loading, and templating.

Astro uses file-based routing to generate pages, rather than dynamic routing. Not only does the file-based method consume less bandwidth, but it also saves you from having to import your components manually.

Here’s an example of defined routes:

```html
src/pages/index.astro => yourdomain.com
src/pages/test.astro => domain.com/test
src/pages/test/subpage => domain.com/test/subpage
```

With these routes, our resulting homepage would be rendered as follows:

```html
<!-- src/pages/index.astro -->
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <title>Hello World</title>
</head>
<body>
    <h1>Hello, Kinsta</h1>
</body>
</html>
```

But we already know how to use layouts, so let’s convert this into something that’s globally accessible:

```jsx
---
import Base from '../layouts/Base.astro';
---

<Base>
    <h1>Hello, Kinsta</h1>
</Base>
```

There – that’s much cleaner.

We’ll discuss routing in Astro in more detail later in this article, but for now, let’s move on to the fun stuff: site construction and customization.

## **Customizing and Extending Astro**

It’s time to learn how to customize your Astro site! We’re going to use Markdown collections, routing, image handling, and an integration with React to build out and personalize our static site.

<aside id="MTUzOTAw"><div><div><a href="https://ace.kinsta.io/editor/load/69" target="_blank">🎈</a><div class="bg-dark article-normal rounded background-image"><div class="flex flex-centered"><div class="container container-centered block-2"><h2>Unlock a new level of performance and ease with Kinsta</h2><p><span>Find out why bloggers, agencies, and Fortune 500 companies are choosing Kinsta to host, build, and launch their websites, applications, and databases.</span></p><div class="button-group"><a href="https://kinsta.com/schedule-demo/" class="button button-secondary "><span>Book a Demo</span></a></div></div></div></div></div></div></aside>

### **Markdown Collections**

With version 2.0, Astro [introduced](https://astro.build/blog/astro-2/#automatic-type-safety-for-markdown--mdx) a much better way to maintain Markdown content than before. Thanks to collections, we can be sure that all our frontmatter data is included and has the correct type of association.

Lately, in version 2.5, they [added a possibility to also manage JSON and YAML files](https://astro.build/blog/astro-250/) as collections.

Ready to get your hands dirty?

First, put all your Markdown articles in the **src/content/collection\_name** folder. We’re going to create a blog collection for this project, so in our demonstration, the folder will be **src/content/blog**.

Now it’s time to define all the required frontmatter fields in our **src/content/config.ts** file. Our blog will need the following:

* `title` (string)
    
* `tags` (array)
    
* `publishDate` (time)
    
* `image` (string, optional)
    

This is what it all looks like put together:

```jsx
import { z, defineCollection } from 'astro:content';

const blogCollection = defineCollection({ 
    schema: z.object({
        title: z.string(),
        tags: z.array(z.string()),
        image: z.string().optional(),
        publishDate: z.date(),
    }),
});

export const collections = {
    'blog': blogCollection,
};
```

And this is what our [**article-about-astro.md**](http://article-about-astro.md) Markdown file contains:

```markdown
---
title: Article about Astro
tags: [tag1, tag3]
publishDate: 2023-03-01
---
## Tamen risit

Lorem *markdownum flumina*, laceraret quodcumque Pachyne, **alter** enim
cadavera choro.
```

True, there’s nothing special about our Markdown file. But there’s some hidden magic here that will manifest if we make a typo.

Let’s say, for instance, that instead of typing `publishDate`, we accidentally typed `publishData`. In the case of a misspelling like this, Astro will throw an error:

```bash
blog → article-about-astro.md frontmatter does not match collection schema.
  "publishDate" is required.
```

Amazing, right? This nifty feature can help us find errors relating to frontmatter in a matter of seconds.

The last thing we need to add is a page showing our data. Let’s create a file at **src/page/blog/\[slug\].astro** with the following code:

```jsx
---
import Base from '../../layouts/Base.astro';
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
    const blogEntries = await getCollection('blog');
    return blogEntries.map(entry => ({
        params: { slug: entry.slug }, props: { entry },
  }));
}
const { entry } = Astro.props;
const { Content } = await entry.render();
---
<Base>
    <h1>{entry.data.title} </h1>
    <Content />
</Base>
```

Thanks to `getStaticPaths`, Astro will create all the static pages for each post in the blog collection.

The only thing we’re missing now is a listing of all our articles:

```jsx
---
import Base from '../../layouts/Base.astro';

import { getCollection } from 'astro:content';
const blogEntries = await getCollection('blog');
---
<Base>
<ul>
    {blogEntries.map(item => <li> <strong><a href={'/blog/' + item.slug}>{item.data.title}</a></strong></li>)}
</ul>
</Base>
```

As you can see, using collections makes this task remarkably simple.

Now, let’s create a data type collection. First, we must open the **src/content/config.ts** file again and add a new data collection:

```jsx
import { z, defineCollection, referenece } from 'astro:content';

const blogCollection = defineCollection({ 
	type: 'content',
    schema: z.object({
        title: z.string(),
        tags: z.array(z.string()),
        image: z.string().optional(),
        publishDate: z.date(),
	    author: reference('authors')
    }),
});

const authorsCollection = defineCollection({ 
	type: 'data',
    schema: z.object({
        fullName: z.string(),
        country: z.string()
    }),
});


export const collections = {
    'blog': blogCollection,
'authors': authorsCollection,
};
```

Apart from creating a new collection, we also added the **author** reference in the **blogCollection**.

Time to create a new author. We must create a file called **maciek-palmowski.json** in the content/authors.json:

```jsx
{
    "fullName": "Maciek Palmowski",
    "country": "Poland"
}
```

The last thing left is to grab this data in our Post. To do so, we’ll need to use [getEntry](https://docs.astro.build/en/reference/api-reference/#getentry):

```jsx
---
import Base from '../../layouts/Base.astro';
import { getCollection, getEntry } from 'astro:content';
export async function getStaticPaths() {
  const blogEntries = await getCollection('blog');
  return blogEntries.map(entry => ({
    params: { slug: entry.slug }, props: { entry },
  }));
}
const { entry } = Astro.props;
const author = await getEntry(entry.data.author);
const { Content } = await entry.render();
---
<Base>
<h1>{entry.data.title}</h1>
<h2>Author: {author.data.fullName}</h2>
<Content />
</Base>
```

### **Routing**

Astro has two different routing modes. We already learned about the first – static (file-based) routing – when we covered pages earlier.

Now we’re going to shift our focus to dynamic routing.

Using dynamic route parameters, you can instruct an Astro page file to automate the creation of multiple pages with the same structure. This is useful when you have a lot of one particular type of page (think author bios, user profiles, documentation articles, and so on).

For this next example, we’ll work on generating bio pages for our authors.

In Astro’s default static output mode, these pages are generated at build time, meaning you must predetermine the list of authors that get a corresponding file. In dynamic mode, on the other hand, pages are generated upon request for any route that matches.

If you want to pass a variable as your filename, add brackets around it:

```html
pages/blog/[slug].astro -> blog/test, blog/about-me 
```

Let’s dive deeper into this using the code from our **src/page/blog/\[slug\]** file:

```jsx
---
import Base from '../../layouts/Base.astro';
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
    const blogEntries = await getCollection('blog');
    return blogEntries.map(entry => ({
        params: { slug: entry.slug }, props: { entry },
  }));
}
const { entry } = Astro.props;
const { Content } = await entry.render();
---
<Base>
    <h1>{entry.data.title}</h1>
    <Content />
</Base>
```

The `getStaticPaths` route is responsible for generating all the static pages. It returns two objects:

* `params`**:** Used to fill the brackets in our URLs
    
* `props`**:** All the values we’re passing to the page
    

And with that, your page generation is taken care of.

### **Image Handling**

We can’t talk about performant websites without bringing up [modern image formats](https://kinsta.com/blog/image-file-types/), correct resizing methods, and lazy loading.

Luckily, Astro’s got us covered here, too. Thanks to the `@astrojs/image` package, we can introduce all the above in a matter of minutes.

After [installing the package](https://docs.astro.build/en/guides/integrations-guide/image/), we gain access to two components: `Image` and `Picture`.

The `Image` component is used to create an optimized `<img />` tag. Here’s an example:

```jsx
---
import { Image } from '@astrojs/image/components';
import heroImage from '../assets/hero.png';
---

<Image src={heroImage} format="avif" alt="descriptive text" />
<Image src={heroImage} width={300} alt="descriptive text" />
<Image src={heroImage} width={300} height={600} alt="descriptive text" />
```

Similarly, the `Picture` component creates an optimized `<picture/>` component:

```jsx
---
import { Picture } from '@astrojs/image/components';
import hero from '../assets/hero.png';
---
<Picture src={hero} widths={[200, 400, 800]} sizes="(max-width: 800px) 100vw, 800px" alt="descriptive text" />
```

### **SSG vs SSR**

By default, Astro runs as a static site generator. This means that all the content is converted to static HTML pages.

While this is a perfect approach from many perspectives (especially speed-related), we might sometimes prefer a more dynamic approach. If you want a separate profile page for each user, for example, or if you have thousands of articles on your site, re-rendering everything each time would be far too time-consuming.

Luckily, Astro also can work as a fully server-side-rendered framework instead or in a hybrid mode between the two.

To enable side-wide SSR, we need to add the following code to **astro.config.mjs**:

```jsx
import { defineConfig } from 'astro/config';

export default defineConfig({
    output: 'server'
});
```

This is the standard approach.

The hybrid approach means that by default, everything is dynamically generated, apart from the pages with `export const prerender = true` added.

With Astro 2.5, there is also the possibility to [set static rendering as default and select dynamic routes manually](https://astro.build/blog/astro-250/#static-by-default-hybrid-rendering-experimental).

Thanks to those, we can, for example, create a fully statically generated website with dynamic login and profile pages. Neat, right?

You can read more about this in the [official documentation](https://docs.astro.build/en/guides/server-side-rendering/).

### **Integrating Other JavaScript Frameworks**

Another amazing feature of Astro allows you to bring along your favorite framework and use it in concert with Astro. You can mix Astro with React, Preact, Svelte, Vue, Solid, or Alpine (for all integrations, see [Astro’s “Add Integrations” documentation](https://docs.astro.build/en/guides/integrations-guide/)).

Let’s say we want to use React. First, we need to install the integration by running the following in [npm](https://kinsta.com/knowledgebase/what-is-npm/):

```jsx
npx astro add react
```

Now that React has been integrated, we can create a React component. In our case, it will be the counter component at **src/components/ReactCounter.tsx**:

```jsx
import { useState } from 'react';

/** A counter written with React */
export function Counter({ children }) {
    const [count, setCount] = useState(0);
    const add = () => setCount((i) => i + 1);
    const subtract = () => setCount((i) => i - 1);

    return (
        <>
            <div className="counter">
                <button onClick={subtract}>-</button>
                <pre>{count}</pre>
                <button onClick={add}>+</button>
                </div>
            <div className="counter-message">{children}</div>
        </>
    );
}
```

Last but not least, we need to place the counter on our page with the following code:

```jsx
---
import * as react from '../components/ReactCounter';
---
<main>
    <react.Counter client:visible />
</main>
```

And voilà: Your React component has been integrated seamlessly into your site.

## **How To Deploy Astro Using Kinsta**

Now it’s time to get our Astro site onto the web. Luckily, Kinsta is the [perfect host for a quick and painless deployment](https://kinsta.com/application-hosting/).

Start by creating a GitHub repository for your site’s files. If you’re not ready to use your own files, you can clone this [Astro starter site template](https://github.com/kinsta/hello-world-astro) our team created.


![GitHub repo of Astro starter site template by Kinsta](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/piaobl51203trnjbrt7u.png)

GitHub repo of Astro starter site template by Kinsta

Once your repo is ready, [log in to MyKinsta](https://my.kinsta.com/), select **Applications** at the left, and then choose **Application** from the purple **Add service** dropdown.


![Add an application in MyKinsta](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4q0ic14tye0r9l9o95xa.png)

Add an application in MyKinsta

The final step involves you supplying Kinsta with the details of your build and deployment.

Most of what you’ll be asked, such as **Process name** and **Payment method**, will have obvious or simple answers. Note that you may leave the **Start command** field empty if you choose; Kinsta will automatically assign `npm start` as the command.

If you’re unsure how to respond to any other prompt, refer to Kinsta’s documentation for [field-specific guidance](https://kinsta.com/docs/add-an-application/) and [Astro deployment examples](https://kinsta.com/docs/astro-static-site/). You can also check out [Astro’s own guide to deploying at Kinsta](https://docs.astro.build/en/guides/deploy/kinsta/).

Once you’ve finished entering your build’s details, click the **Confirm payment method** button to initialize your build.

And that’s it! You now have a live, fully functioning static site created with the Astro framework.


![A dark page with the Kinsta logo in white in the center above the words "Welcome to Your New Astro Site](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ylr2rnco6j5jgfx7vyq.png)

Our live Astro homepage

You can find your live URL and other deployment details under **Deployments** in your MyKinsta account.


![The "Deployments" screen of MyKinsta showing the details and history of our Astro site deployment.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwgj2btvyeityfzg8k8s.png)

A successful Astro deployment

## **Summary**

Astro’s clear structure, simple syntax, and global components make building and running an application really easy. , Its lightweight nature and dual use of static and dynamic routing dramatically increase site responsiveness, while its capacity for cooperating with and alongside other JavaScript frameworks makes it all the more appealing to experienced coders.

If your goal is to create a content-rich site that loads quickly, grants modular functionality, and provides both static and dynamic generation, then Astro could be the right choice for you.

You can host your static website with [Kinsta’s Application Hosting for free](https://kinsta.com/application-hosting/), and if you like it, upgrade to our [Hobby Tier](https://kinsta.com/application-hosting/pricing/hobby-tier/) plan.

💡 What are your thoughts on the Astro static site generator? Have you used it on a project of your own? Let us know in the comments section below.