Edit and preview content side-by-side with our new Live Preview

What is Astro - the JS framework? A guide to get you started

Learn how Astro's static-first approach and island architecture make it a standout choice for developers seeking high performance and flexibility.
Motunrayo Moronfolu

Written by Motunrayo Moronfolu

Aug 08, 2024
astro, javascript

Developers often claim that the most challenging task in development is choosing good variable names. However, before reaching that point, it is crucial to select the right development tools, libraries, and frameworks that will enhance performance, streamline the development process, and provide a better user experience.

The vast number of frameworks available makes this decision even more complicated, often leading to choice fatigue. JavaScript is particularly affected, with new frameworks, tools and libraries frequently entering the ecosystem.

One of these frameworks is Astro, which ranks second in the 2023 JavaScript Rising Stars survey and is often referred to as the "framework of all frameworks.". This article will explore Astro, its features, why it is called the "framework of all frameworks," and how to get started.

#What is Astro?

Astro is an open-source web framework created by Fred Schoot in 2021 to build fast, performant, content-driven websites.

Astro is well known for its performance, which is largely hinged on its static-first architecture. This architecture renders websites without any JavaScript, significantly reducing load times. However, Astro’s popularity in the developer ecosystem can be traced to its framework-agnostic approach, which allows developers to choose their preferred tools while still benefiting from Astro's features.

In the next section, let’s explore these features in more detail.

#What’s unique about Astro?

Astro is unique among existing JavaScript web frameworks, and by exploring its features, you’ll discover why.

Island architecture

Island architecture, also known as Astro islands, is a metaphor for frontend architecture that conveys two key concepts: separation of concerns and independent functionality.

In Astro, most web pages are pre-rendered as static HTML at build time, minimizing unnecessary JavaScript. Interactive components, like widgets or dynamic content, are isolated as "islands" within the static content, loading only the necessary JavaScript for their specific functionality. This keeps the bulk of the webpage fast and lightweight. Examples of solutions using this include Deco and Dodonut.

Island architecture

For instance, a blog with a static layout can have interactive elements like a search bar or comment section as separate islands with their own JavaScript, ensuring lightweight websites. Astro also uses partial hydration, where only the interactive parts of a webpage are made dynamic, further optimizing performance.

Outputs no JS by default

JavaScript is one of the web's three core pillars and is crucial for web interactivity. However, it can slow down initial page loads, especially for content-heavy sites.

Many frameworks rely heavily on JavaScript for rendering and interactivity. However, Astro takes a different approach by defaulting to zero JavaScript output. Astro sends only HTML and CSS to the browser when a webpage loads. JavaScript is included only if explicitly needed, enhancing performance significantly. This approach also improves SEO, as search engines can easily index static content, potentially boosting site visibility.

Server side approach

By default, Astro operates as a Static Site Generator (SSG), which means the website's pages are created as static HTML files during the build process. These static files are typically stored on a Content Delivery Network (CDN) and are then served directly to the user's browser, resulting in faster loading times.

In addition to SSG, Astro supports Server-Side Rendering (SSR). In SSR mode, the server dynamically generates HTML for each page upon request. This capability is beneficial for applications requiring real-time data updates or personalized content.

Agnostic to UI libraries and frameworks

Most JavaScript frameworks enforce the use of a specific UI library. For example, choosing Next.js generally restricts developers from using React libraries, preventing them from using Vue libraries within the same project.

Astro, however, is not a frontend framework as it does not mandate the use of any specific UI library or framework-specific capabilities. With compatibility built into its core, Astro allows developers to choose and combine various libraries and frameworks, such as React, Vue, and Svelte, to suit their project’s needs.

This flexibility means developers do not need to learn a new library from scratch and can reuse existing components from different frameworks within Astro.

#Comparison to other frameworks

Astro is often referred to as "the framework of all frameworks" because it uniquely combines the strengths of multiple JavaScript frameworks while addressing some of their common drawbacks, like vendor lock-in and performance overhead of frameworks like Nuxt.js.

Here is a breakdown of how Astro compares to other popular frameworks across some key features:

ComparisonsAstroNuxt.jsGatsbyNext.js
SSR
SSG
Partial hydration
Island architecture

Choosing the best framework depends on each project's requirements. While Next.js, Nuxt.js, and Gatsby shine for their SSR and SSG capabilities, Astro distinguishes itself by combining these strengths into a single tool, especially for content-focused websites and websites prioritizing performance and efficiency.

#Basic step for setting up an Astro project

Setting up an Astro project can be done via the command line. Here is a step-by-step guide to get started:

1. Prerequisites

The following are required on your computer to get started:

  • Node.js(Version 18 or higher)
  • Basic knowledge of JavaScript
  • An integrated development environment (IDE) like VSCode

2. Creating an Astro project

First, create an Astro project using:

npm create astro@latest

Running the command above will prompt you to select from several options.

undefined

Choose the options that best fit your project. Next, switch to the project folder you created and open in your IDE.

To run the project in developer mode, run npm run dev in the CLI. This will provide a URL showing the Astro project when opened in the browser.

#Project structure in Astro

Understanding Astro’s organized project structure is crucial for efficient development. Astro’s structure provides clear code organization, which allows the developer to focus on building and improving the site rather than managing a disorganized codebase.

Folder structure

An Astro project directory after creation will look like this:

/my-astro-project
├── node_modules/
├── public/
├── src/
│ ├── components/
│ ├── layouts/
│ ├── pages/
├── env.d.ts
├── .gitignore
├── astro.config.mjs
├── package.json
├── package-lock.json
├── README.md
└── tsconfig.json

As seen above, the Astro root folder consists of various directories and files. Let us examine some of these directories.

  • public/: This contains the static files that do not need processing.
  • src/pages/: The files here are automatically turned into routes.
  • src/components/: This stores the reusable components. For example, it may contain a Header.astro for the site’s header.
  • src/layouts/: Layouts help structure the pages. A typical example is MainLayout.astro, which might include a header, footer, and slot for page content.

The src directory contains the project source code and is where most of the development time will be spent.

Routing

Astro's routing is designed to be simple and intuitive. It achieves this through file-based routing. At the basic level, all routing between pages uses the standard HTML <a> tags with the href attribute pointing to the specified route. However, Astro also supports other types of routing, like dynamic and static routing.

Static routing involves converting all files in the src/pages directory with the .astro, .md, or .mdx extensions into static pages. For example, src/pages/blog.astro becomes /blog.

On the other hand, dynamic routing involves using square brackets [] to create routes that can handle variable segments in the URL. For example, src/pages/blog/[slug].astro would match any URL starting with /blog/, followed by any value such as /blog/first-post. The value inside the brackets becomes a parameter that can be used in the page code.

Beyond this, creating nested routes using folders within src/pages is also possible.

As seen in all the examples above, Astro does not need any special configuration to create routes. Similar to Next.js or Nuxt.js, one can easily set up the routes, by simply organizing files within the src/pages directory.

Pages

Pages constitute the routes are found in the src/pages/ directory of any Astro project. Each page in Astro is created as a .astro file, with each of these files representing a different view or sections that users navigate to on the website. For example:

  • index.astro corresponds to the homepage (/)
  • about.astro corresponds to the about page (/about)

This file-based structure in Astro directly correlates with the URL paths of the web application, which ensures clarity and ease of development.

Components

Astro components are reusable, self-contained units of code that encapsulate parts of the user interface, similar to those in modern web frameworks like Next.js and Nuxt.js. They support various JavaScript frameworks, allowing developers to use React, Vue, Svelte, and others alongside Astro’s native components.

Astro’s native components use the .astro file extension and are typically located in the src/components directory. They resemble HTML files but include additional features like frontmatter, slot support, and scoped CSS.

A simple Astro component will look like this:

---
// Frontmatter section
const title = "Hello, World!";
---
<style>
h1 {
color: purple;
}
</style>
<h1>{title}</h1>
<p>This is an Astro component!</p>

In the example above, a constant title was defined in the frontmatter with the value "Hello, World!". Scoped CSS was added to style the <h1> element, ensuring the styles apply only to this component. Finally, HTML that uses the title variable was included.

To use this component elsewhere, write:

---
import Greeting from '../components/Greeting.astro';
---
<Greeting />

This approach supports Don't Repeat Yourself (DRY) principles and separation of concerns.

Layouts

Layouts found in src/layouts in an Astro project allow developers to define a common and consistent design and structure, such as headers, footers, or navigations, that can be reused across different pages, ensuring that the website has a cohesive look and feel.

While layouts and components are essential in an Astro project, components handle reusable UI elements while layout manage the overall page structure.

Layouts contain a <slot/> element, which acts as a placeholder for injecting each page's unique content. Let us explore a simple layout example.

<header>
<h1>Hygraph</h1>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
<main>
<slot />
</main>
<footer>
<p>&copy; 2024 Hygraph</p>
</footer>

Next, developers can use the structure defined above in pages. Like so:

---
import WebsiteLayout from '../layouts/WebsiteLayout.astro';
---
<BaseLayout>
<h2>Welcome to My Website</h2>
<p>This is the home page.</p>
</BaseLayout>

Developers can also create multiple layouts for different sections of the site. For instance, one might have a blog layout for blog posts and another for other parts of the website.

Styling

Styling in Astro is flexible, and developers can use various methods to apply CSS to any components and pages. This flexibility is beneficial as it lets developers choose the approach that best fits the project's needs - whether it is scoped CSS within components (as seen in an earlier example), global styles, or CSS frameworks like TailwindCSS.

Images

Images are crucial for any website's visual appeal and user experience. Astro supports the standard HTML <img> tag for static images but offers a built-in <Image/> component for optimizing larger images and improving performance.

Astro also enables resizing images with CSS attributes like width and height, and supports lazy loading for improved loading times.

To meet accessibility standards, Astro requires the alt attribute for all images. For decorative images, Astro recommends using empty quotes “alt=""” to indicate their purpose without disrupting screen reader experiences.

#Integrations and extensibility

Astro’s is called the "framework of all frameworks" because of its flexible and extensible design, which allows smooth integration with various tools, libraries, and services. This section explores how Astro can be integrated and extended to suit different project needs.

Integrating other frameworks

Astro adheres to a “bring your favorite tools” philosophy, which can account for part of its growing popularity. To allow this, Astro provides official integrations for various libraries and tools like Vue, React, and more. These integrations can be installed using one of the following methods:

  • Using the astro add command:
npx astro add <framework>

In this case, add React by running npx astro add react. Running this command will give you a couple of prompts, which, after responding, will add the desired framework to the project.

  • The second option is through manual integration. This involves adding the integration to the integrations property in the astro.config.mjs file. Like so:
//astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({ integrations: [react()], });

This will add the required dependencies needed to run React in an Astro project.

Once the integration is installed, one can create, import, and use components from the respective framework within Astro components. Here is an example of creating and using a React component in Astro:

// src/components/Counter.jsx
import { useState } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
};
export default Counter;

First, a file called Counter.jsx was created, and the code above was created. This code creates a counter component in React that displays the number of times a button is clicked.

To use this component in an Astro file, do the following:

---
import Counter from '../components/Counter.jsx';
---
<Layout title="Welcome to Astro.">
<main>
<h1>Click Me!</h1>
<Counter client:load />
</main>
</Layout>

To make any framework component interactive, you need to use a client: directive, which specifies when the component's JavaScript should be sent to the browser. Similarly, in the above code, client:load directive was added, which ensures the component is hydrated on the client side after the initial page load.

Editor's Note

The React component was created using the proper React extension—.jsx. Every framework used in Astro must adhere to its extension. For Vue or Svelte components, the respective file extensions (.vue or .svelte) would be used.

Refer to the Astro integration guide for more detailed information and other integration options.

Fetching data

Fetching data from external or internal sources and interacting with APIs is a core part of building any modern web solution. Astro supports this natively using the fetch function, which provides a familiar way to make HTTP requests to APIs.

Here’s a basic example of how to use the fetch function in an Astro component:

---
const response = await fetch('https://api.example.com/data');
const data = await response.json();
---

In this example, the fetched data can be used within the component as needed.

The fetch function in Astro is versatile. It can query both REST and GraphQL APIs. It is available within any Astro or external components to retrieve data asynchronously. This flexibility helps to integrate data fetching seamlessly into Astro projects.

A Content Management System (CMS) proves helpful here as it simplifies content creation and management.

Managing content from a CMS

Integrating CMS with Astro is useful for sites that require frequent content updates without compromising performance. While the CMS handles content creation and storage, Astro supports SSG and fetches the data from the CMS for display on the website.

The workflow for integrating any CMS with Astro typically involves:

  1. Selecting a suitable CMS
  2. Installing the Astro integration package
  3. Configuring the CMS client within the Astro project
  4. Using integration functions to fetch and display content.

Best practices for using any CMS with Astro include implementing caching strategies to optimize performance and setting up error handling for reliable data access.

Now, let us pick a CMS and integrate it with Astro.

Pulling content from Hygraph via GraphQL

Choosing the right CMS is crucial for the business. In this section, you will use Hygraph, a federated headless CMS that uses GraphQL for content delivery.

To get started, sign up for a free-forever developer account on Hygraph. Hygraph also provides starters for Astro, and for this section, SKNCRE starter—a composable cosmetics brand e-commerce demo will be used.

Setting up the Hygraph project

  1. Clone the project: Clone the SKNCRE starter project. It may take a while, but once cloned, open the project.

  2. Configure API access: Navigate to Project Settings > API Access > Public Content API. Here, configure the Public Content API permissions to allow read requests without authentication. If you haven't already, click “Yes, initialize defaults” to add the required permissions for the “High Performance Content API.”

  3. Set environment variable: In the same section as the above, locate the High Performance Content API and copy the URL. Next, create a .env file in the Astro root project you created before and add the URL, like so:

`HYGRAPH_HIGH_PERFORMANCE_ENDPOINT=***`

Now, Hygraph is successfully connected to your Astro project.

Before working on the Astro project, let us explore the cloned Hygraph project. To do this, navigate to the Hygraph’s API playground and explore the available content. In this case, you will query the products with a focus on specific parameters, such as:

query MyQuery {
products {
name
price
shortDescription
stock
images {
url
alt
}
}
}

Running the query in the API playground displays the response in the pane. This content is sourced from the Hygraph project and its accessible via the “Content” view.

Next, execute the same query in the Astro project and showcase the content in the UI.

Querying data from Hygraph

Follow this installation guide to create a new Astro project that uses Tailwind for styling.

Next, install the GraphQL client for fetching data:

`npm add graphql-request`

Now, create the first Astro component named ProductItem.astro and add this to the file:

---
import { Image } from 'astro:assets';
export interface Product {
name: string;
price: number;
shortDescription: string;
stock: number;
images: {
url: string;
alt: string;
}[];
}
const { name, price, shortDescription, stock, images } = Astro.props;
---
<div class=" border border-stone-300 border-solid p-4 mb-4">
<div class="flex">
{images.map((image) => (
<Image src={image.url} alt={image.alt} width="50" height="50" loading="lazy" class="w-1/4 mr-2"/>
))}
</div>
<h2 class="text-2xl my-2 capitalize">{name}</h2>
<p class="mb-2">{shortDescription}</p>
<p class="font-bold mb-2">Price: ${price}</p>
<p class="mb-2">Stock: {stock}</p>
</div>

In the above code:

  • An interface was declared to specify the expected shape of product data as props, including fields like name, price, short description, stock, and an array of images
  • These properties were then destructured from **Astro.props** for use within the component
  • Finally, the HTML section was created to display the data, styling it with CSS.

Next, let us create another component named **ProductList.astro** and add the code below:

---
import ProductItem from './ProductItem.astro';
export interface Product {
name: string;
price: number;
shortDescription: string;
stock: number;
images: {
url: string;
alt: string;
}[];
}
const { products } = Astro.props;
---
<div class="flex md:flex-row flex-col gap-4 justify-between">
{products.map((product: Product) => (
<ProductItem {...product} />
))}
</div>

Here, a ProductItem component was imported, and a Product interface was defined to structure the product data. The component receives a list of products via Astro.props and renders each product using the ProductItem component, displaying them in a flexible and responsive layout.

Fetching and displaying data

Now, let us bring it together by adding the code below to the index.astro:

---
import { GraphQLClient } from "graphql-request";
import ProductList from '../components/ProductList.astro';
// Create a new GraphQL client with the Hygraph endpoint
const client = new GraphQLClient(import.meta.env.HYGRAPH_HIGH_PERFORMANCE_ENDPOINT);
// Declare Products type
type Product = {
name: string;
price: number;
shortDescription: string;
stock: number;
images: {
url: string;
alt: string;
}[];
};
type Products = {
products: Product[];
};
// Fetch the data via the GraphQL query
const { products }: Products = await client.request(`
query MyQuery {
products {
name
price
shortDescription
stock
images {
url
alt
}
}
}
`);
---
<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</title>
</head>
<body>
<div class="m-12">
<h2 class="mb-8 text-5xl font-bold">Available Products:</h2>
<ProductList products={products} />
</div>
</body>
</html>

In this code, the following was achieved:

  • Imported the GraphQL Client installed in earlier steps and the ProductList component created earlier
  • Initialized the GraphQL client using the endpoint stored in the environment variable HYGRAPH_HIGH_PERFORMANCE_ENDPOINT
  • Defined the TypeScript types for the product and list of products
  • Fetched the product data from Hygraph using the GraphQL client
  • Destructured the data to extract the products
  • Used the ProductList component to render the fetched products, passing the products array as a prop

Editor's Note

Our project was set up with TypeScript; hence, all the code was written to suit that.

At this point, our UI should look like the below:

Products fetched from Hygraph

After exploring seamless data fetching from Hygraph (headless CMS) in an Astro project, there is much more developers can achieve with Hygraph and Astro in terms of content management

#Deploying Astro

Now that you have created the project, the next step is to deploy it so users can access it online. To achieve this, Astro supports various deployment options, including Netlify, GitHub Pages, Vercel, and more.

In this guide, you will use Vercel to deploy the application. The application built is a static site, and since Astro generates static sites by default, the deployment process is straightforward and requires minimal configuration.

Vercel offers two deployment methods: the Vercel UI and the Vercel CLI. Here, the focus will be on Vercel UI.

Steps to deploy using Vercel UI

  1. Ensure the Astro project is pushed to a Git repository, like GitHub. Find the project you created in this GitHub repository
  2. If you don't already have one, sign up for a Vercel account
  3. Log in to Vercel and click on "Import Project". Then, select the Git repository where the Astro project is pushed. Vercel will automatically detect the framework (Astro) and set up the project accordingly
  4. Add the environment variables as needed and deploy

Once the deployment is complete, you will see a message indicating the application the deployment status. In our case, the deployed application can be accessed via this Vercel link.

For guidance on deploying Astro projects to Netlify, refer to the Astro-Hygraph deployment documentation.

Now that you have successfully deployed the project, let's consider the cases where Astro is most suitable.

#When to use Astro

Astro stands out for its numerous features, which have been explored in previous sections. However, there are some cases where Astro excels more than other frameworks. Some of these include:

  1. Content-focused websites: Astro’s SSG features ensure pages load swiftly by pre-rendering HTML during the build process. This makes it ideal for content-heavy websites that serve a lot of static content, such as documentation sites, where the primary focus is delivering static content quickly.
  2. SEO-focused websites: Astro's ability to output no JavaScript by default enhances page load times and improves SEO. Faster-loading pages have more chances to rank higher in search engine results, making Astro a great choice for websites that rely heavily on search engine visibility.
  3. Performance-centric websites: Astro's focus on SSG and minimal JavaScript by default leads to blazing-fast loading times, making it a great choice for websites where performance is paramount. This ensures a better user experience and lower bounce rates.
  4. Integrating with Headless CMSs: Astro's compatibility with headless CMSs like Hygraph makes Astro a great choice for projects that require efficient content management and seamless integration with a headless CMS.
  5. Flexibility preference: Astro's agnostic nature towards UI libraries and frameworks allows developers to use React, Vue, Svelte, or other frameworks together. This flexibility is beneficial for projects that require integrating various technologies or for teams with diverse expertise.

#When not to use Astro

While Astro is a powerful and versatile framework, there are certain scenarios where it might not be the best development option. Some of these include:

  1. Single-Page Applications (SPAs): Astro is a Multi-Page Application framework. As such, it is not well suited for highly interactive SPAs where client-side routing is crucial. In this case, React or Vue might be more suitable.
  2. Applications with complex state management: Astro might not be the best fit for applications with complex state management requirements and heavy client-side interactions compared to Nuxt.js or Next.js.
  3. Heavy client-side computation: Applications that require heavy client-side computation, such as data visualization or complex calculations, may not benefit from Astro's strengths. In these cases, an SPA framework with more client-side capabilities might be a better fit.

Astro is not a one-size-fits-all solution; it is important to know where its strengths lie and when it is best to use a different framework.

#Tips and best practices

Having explored various aspects of Astro, let’s look at some recommended practices that will enhance your product

  1. When working with images, place images in the src folder rather than the public folder. This allows Astro to process and optimize images, enhancing performance. Files in the public folder are not processed, which can lead to performance declines.
  2. Leverage the frontmatter in the Astro files to include metadata such as titles, descriptions, and other SEO-related information. Properly structured metadata helps improve the site's SEO,
  3. Use scoped CSS within Astro components to avoid style conflicts and ensure that components are styled independently. This approach adheres to the principle of separation of concerns, making the codebase cleaner and more maintainable.
  4. While Astro provides built-in SEO features, it is also beneficial to use semantic HTML elements like <section>, <nav>, and <article>. These elements enhance accessibility and improve SEO.
  5. Astro is under active development. Follow the official Astro channels and regularly check the releases to stay updated with the latest features, improvements, and bug fixes.

#Wrapping up

This article explored Astro, highlighting its importance and best use cases. A practical example using Hygraph demonstrated how to leverage Astro's static site generation capabilities. However, this is just the beginning of what one can achieve with Hygraph.

To explore the full range of possibilities with Hygraph, consider watching Hygraph's Head of Developer Relations, Bryan Robinson’s guides on "Building Websites with Astro and Hygraph Part 1" and "Building Websites with Astro and Hygraph Part 2."

Additionally, join Hygraph’s developer workspace to connect with other users and the Hygraph team.

Blog Author

Motunrayo Moronfolu

Motunrayo Moronfolu

Technical writer

Motunrayo Moronfolu is a Senior Frontend Engineer and Technical writer passionate about building and writing about great user experiences.

Share with others

Sign up for our newsletter!

Be the first to know about releases and industry news and insights.