Writing Newsletter Websites with Astro

Written byKalanKalan
💡

If you have any questions or feedback, pleasefill out this form

This post is translated by ChatGPT and originally written in Mandarin, so there may be some inaccuracies or mistakes.

When I was helping to write the website for my newsletter, I realized that routing and isomorphic rendering were not my main concerns, and I didn’t have a need for frequent database interactions or browser-side interactivity. At that time, I wondered if a SSR framework like Next.js was overkill. Although Next.js supports static site generation, the mental overhead of writing React components still made me hesitate.

I first heard about Astro from a colleague and thought it was a rare opportunity to give it a try. Astro offers a development approach centered around SFCs (Single-File Components), allowing you to build web pages quickly and easily. Its architecture aligns with cloud trends, making adjustments for services like Vercel, Netlify, and GitHub Pages straightforward—just upload your code, and you can create a fully functional web page.

astro-deploy

There are many static site generators on the market, and you might be wondering why we need another one. My blog has evolved from Jekyll and Hexo to Gatsby, and I've experimented with several framework transitions. This experience has clarified what I value most when creating content-driven websites.

Jekyll and Hexo share many similarities. While they provide the basic functionality for building content sites, extending Markdown syntax or implementing more interactive MDX features is relatively challenging, and you must adapt to their inherent templating syntax.

With Gatsby, you need to write GraphQL yourself. Initially, this flexibility is enjoyable, but without a defined schema, adding frontmatter properties to Markdown can become quite cumbersome.

Over time, I often found myself in a situation where I just wanted to write a simple page but ended up spending a lot of time writing GraphQL and React components.

Another option is 11ty, which is similar to Astro, but I believe the biggest difference lies in the templating syntax. 11ty supports multiple templating syntaxes, but having spent many years writing in React, JSX feels the most intuitive to me, which is exactly what Astro provides. Below are some key differences I find evident between Astro and other generators.

Single-File Components

In Astro's design, each file can act as a complete component.

This means you can write JavaScript (which only runs at build time), HTML, and styles in the same file, allowing the framework to handle class name isolation. If you're familiar with writing JSX or Vue, you can easily write Astro components without needing to learn a different templating syntax.

---
const variable = 'Hello';
---

<section>
  <p>
    {variable}
  </p>
</section>

<style>
  p { font-size: 14px; }
</style>

Being able to write JavaScript, HTML, and CSS in the same file while developing web pages is quite comfortable, and Astro supports this by default without requiring additional configuration.

Although the component-building approach is similar to Vue or Svelte, it’s important to note that Astro is neither SSR by default nor a frontend framework, so pages will only execute at build time, with no additional JavaScript produced. (unless SSR mode is enabled)

What if you need to run JavaScript in the browser for interactivity? Astro provides two options:

  • Write it inside a <script> tag, but since there's no DOM binding feature, you'll need to write code like document.querySelector()
  • Directly import components from other frontend frameworks

Astro Island

Sometimes, we have a requirement where most of the pages are static, but one or two pages or parts of a page need to be highly interactive, like a blog comment section.

Astro allows you to reference component files from other frameworks, such as React or Vue, as long as you have the corresponding adapter. This feature is quite special to me, adding flexibility to static site generators.

<aside>
  <ReactOrVueComponent client:load />
</aside>

In this code snippet, when encountering a non-Astro component, Astro will create an empty DOM during compilation. The JavaScript will load in the browser, and then the component’s DOM nodes will be mounted. The client:load hint indicates when the script should be loaded. I find the inclusion of options like client:media and client:visible particularly thoughtful.

You might encounter components that you only want to load on specific devices, such as a collapsible menu only on mobile screens, or components that should only load when a user scrolls to a certain position. Without framework assistance, you'd have to write additional JavaScript to handle these cases, but Astro simplifies this for you.

There’s a question about how different frameworks (e.g., Astro and React) can share state. Astro already provides a solution, though introducing another library may raise concerns about whether the cost outweighs the benefits.

I view the support for integrating other frontend frameworks within static site generators positively. Astro may already meet the needs of most websites, and if you're looking to create a purely interactive site, you probably wouldn’t choose Astro from the start. However, adding frontend framework support does cater to a niche of website requirements, allowing for quick site creation, and integrating Preact for interactivity is also quite straightforward when needed.

Content Collections

Astro features a capability called Content Collections. Simply put, this helps you query static Markdown files. Files under the content/ directory are treated as Content Collections.

import { getEntry } from "astro:content";
const entry = await getEntry("weekly", "my-first-weekly.md");

const { Content } = await entry.render();

While Astro can directly render Markdown as a page, if you want to retain more flexibility for page adjustments, it also offers built-in lookup functions for querying. entry.render() returns a processed <Content /> component that you can place wherever you want, rendering the Markdown content as HTML.

Type Support

Astro supports TypeScript. Even though it's for static pages, you can still define what properties a component can accept by declaring props, making it much more convenient.

// MyComponent.astro
export interface Prop {
  user: string
}

// <MyComponent user="kalan" />

In addition to defining component properties, content can also be defined. For instance, when defining articles, we typically use frontmatter to set some metadata, such as title, summary, and images:

---
title: This is the title
summary: This is the summary
image: https://image.com
---

Astro provides schema definition functionality (implemented with zod). By defining the schema type in content/config.ts, you can get 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,
};

// Calling in 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, coming from the above weeklyCollection

My Experience with Astro

Overall, I find Astro to be quite comfortable. If you don’t need any special features, treating it like HTML that can take variables works perfectly fine.

The functionalities of static site generators are generally similar. The features mentioned above can indeed be found in other tools. So what sets Astro apart? I believe it lies in the comfortable developer experience, the balance of concise and flexible syntax (JSX), well-thought-out designs for specific scenarios (support for other frontend frameworks and diverse script loading methods), and seamless integration with cloud services.

The way frontend developers build static websites has changed significantly over the years. Since the advent of Gatsby, developers have been seeking more efficient ways to construct web pages—not just enabling server-side rendering of components. Factors like JavaScript loading timing, prefetching, caching via Service Workers, image optimization, style management, edge computing, and maintaining development speed are all considerations modern static site generators must take into account. It’s no longer simply about converting Markdown into HTML and uploading it to a server.

If you found this article helpful, please consider buying me a coffee ☕ It'll make my ordinary day shine ✨

Buy me a coffee