When I was building my newsletter website, I realized that Routing and Isomorphic were not my main focus. I didn't have the need to frequently fetch data from a database or interact with it on the browser side. So, I wondered if a powerful SSR framework like Next.js was overkill. Although Next.js also supports full static output, the mental burden of writing React components made me hesitate.
I first heard about Astro from a colleague and thought it was worth a try. Astro revolves around the concept of Single-File Components (SFC), which allows easy and quick construction of web pages. It also aligns with the trend of cloud integration, making adjustments for services like Vercel, Netlify, and GitHub Pages. By simply uploading the code, you can create a functional website.
You might wonder why we need another static site generator when there are already many in the market. From Jekyll and Hexo to Gatsby, I have gone through several framework transitions for my blog. This experience has made me more aware of what I value when building content-oriented websites.
Both Jekyll and Hexo have many similarities. While they provide basic functionality for building content websites, extending Markdown syntax or achieving higher interactivity with MDX can be challenging. Additionally, they require adapting to their inherent template syntax.
When using Gatsby, you need to write GraphQL yourself. Initially, this flexibility was enjoyable, but it became quite cumbersome when I wanted to add frontmatter properties to Markdown without defining a schema.
Over time, I often found myself in a situation where I spent a lot of time writing GraphQL and React components when all I wanted was to write a simple page.
Another option is 11ty, which is similar to Astro. However, the main difference, in my opinion, lies in the template syntax. While 11ty supports various template languages, as someone who has been writing React for years, JSX seems to be the most intuitive way, and Astro precisely achieves that. Here are some notable differences I found between Astro and other generators.
Single-File Components
In Astro's design, each file can be treated as a complete component.
This means that JavaScript (which only runs at build time), HTML, and styles can be written in the same file, allowing the framework to handle class name isolation. If you are familiar with writing JSX or Vue, you will find it easy to write Astro components without the need to learn additional template syntax.
---
const variable = 'Hello';
---
<section>
<p>
{variableA}
</p>
</section>
<style>
p { font-size: 14px; }
</style>
Being able to write JavaScript, HTML, and CSS in the same file while building a web page is quite convenient, and Astro supports it by default without any additional configuration.
Although the way of constructing components is similar to Vue or Svelte, it is important to note that Astro is not SSR by default, nor is it a frontend framework. Therefore, pages only execute at build time, and no additional JavaScript is outputted (unless in SSR mode).
So, how can we execute JavaScript on the browser for interactivity? Astro provides two options:
- Write the JavaScript code inside
<script>
. However, since there is no DOM binding functionality, you need to write code likedocument.querySelector()
. - Import other frontend frameworks directly.
Astro Island
Sometimes, we have a requirement where most pages are static, but one or two pages or parts of a page require a lot of interactivity, such as a comment section on a blog.
In Astro, you can include components from other frameworks, such as React or Vue, for browser interactivity. As long as there is a corresponding adapter, you can integrate them. This feature is quite special and adds flexibility to static site generators.
<aside>
<ReactOrVueComponent client:load />
</aside>
In this code snippet, when encountering non-Astro components, Astro will create an empty DOM during compilation, load the JavaScript on the browser, and mount the component's DOM nodes. The client:load
indicates when the script should be loaded. What I find thoughtful is that Astro provides client:media
and client:visible
options.
Sometimes, you may want to load components only on specific devices, such as a scrollable menu on mobile screens, or only when a certain position is reached on the page. Without framework assistance, you would have to write additional JavaScript to handle these scenarios. However, Astro takes care of it for you.
One question arises: how can different frameworks (e.g., Astro and React) share state? Astro provides a solution, although it introduces another library, which may raise concerns about the trade-off between the benefits and costs.
I have a positive view of adding support for other frontend frameworks within static site generators. Perhaps Astro already satisfies the requirements of most websites, and if you truly want to build an interactive website, you wouldn't have chosen Astro in the first place. Adding support for frontend frameworks partially fulfills the needs of some websites, allowing for quick website development, and it's quite effortless to incorporate interactivity using Preact when needed.
Content Collections
Astro has a feature called Content Collections, which simplifies querying static Markdown files. Files under content/
are treated as Content Collections.
import { getEntry } from "astro:content";
const entry = await getEntry("weekly", "my-first-weekly.md");
const { Content } = await entry.render();
Although Astro can directly render Markdown as a page, it also provides built-in functions for more flexible page adjustments. entry.render()
returns a preprocessed <Content />
component that can be placed wherever you want, rendering the Markdown content as HTML.
Type Support
Astro supports TypeScript, allowing you to define the attributes a component can receive through props, even for static pages. This makes it much more convenient to use.
// MyComponent.astro
export interface Prop {
user: string
}
// <MyComponent user="kalan" />
In addition to defining component attributes, content can also be defined. For example, in defining an article, we usually use frontmatter to define meta information such as title, summary, and image:
---
title: This is the title
summary: This is the summary
image: https://image.com
---
Astro offers Schema definition functionality (implemented using zod). By defining the Schema type in content/config.ts
, you can obtain the corresponding type when using getCollection
or getEntry
.
import { z, defineCollection } from "astro:content";
const weeklyCollection = defineCollection({
type: "content",
schema: z.object({
published_at: z.date(),
issue_num: z.number(),
title: z.string(),
description: z.string().optional(),
image: z.string().optional(),
}),
/* ... */
});
export const collections = {
weekly: weeklyCollection,
};
// Call from other Astro components
import { getCollection, getEntry } from "astro:content";
const entry = await getEntry("weekly", 'my-first-weekly.md'); // entry will resolve to the weekly type defined in the weeklyCollection above
Astro Experience
Overall, Astro provides a comfortable development experience. If you don't need anything special, you can simply write it as HTML with variables. The functionality of static site generators is quite similar, and the mentioned features can be achieved with other tools. So, what makes Astro stand out? I believe it's the comfortable developer experience, the combination of concise and flexible syntax (JSX), well-designed features for specific scenarios (support for other frontend frameworks and multiple script loading methods), and integration with cloud services.
The way frontend developers build static websites has undergone significant changes in recent years. Starting from Gatsby, developers have been searching for more efficient ways to construct web pages, not just for server-side rendering of components. Modern static site generators need to consider various functionalities such as JavaScript loading time, prefetching, caching through Service Workers, image optimization, style management, edge computing, and development speed. It's no longer just about converting Markdown to HTML and uploading it to a server.