Astro just makes sense

Astro just makes sense

Why wasn't everyone doing it this way all along?

ยท

5 min read

With Astro being days away from a stable 1.0 release, I thought I'd talk about why it's so exciting. I've been playing with Astro for a few months now, using it for every opportunity I get.

If you've never heard of Astro, it's a new metaframework built on top of Vite. I know! Some of you are probably rolling your eyes at the thought of yet another framework, but hear me out. It just makes sense!

Minimal API surface

Framework churn is a real thing. No one wants to learn a new tool just to see it go away in a year. But with Astro, there is barely anything to learn.

The markup syntax used in .astro files is literally just HTML with the ability to use JS expressions inside {curlies}. It's like JSX but without the weird quirks. You get to use regular <svg> attributes and <style> tags like normal HTML. No more className or {' '}.

Besides the markup, all the code that runs before the page generation goes in the "frontmatter" at the top of the file. Any variables declared here will be available in the markup. And because we have access to the full power of JavaScript, we can do all the standard stuff like import and fetch and await.

---
import { format, parseISO } from 'date-fns';

const data = await fetch('/my-api').then(r => r.json());
const { title, content, publishDate } = data;

const publishDateParsed = typeof publishDate === 'string' && format(parseISO(publishDate), 'MM/dd/yyyy');
---

<h1>{title}</h1>
{publishDateParsed && <aside>Published on: {publishDateParsed}</aside>}
<p>{content}</p>

It feels like modern-day PHP, and I mean that in the best way possible. ๐Ÿ˜„

There is so much going on in that little snippet:

  • ESM imports
  • npm packages
  • web fetch API
  • top level await
  • typescript!

All of those pieces feel natural and make sense but if you try to assemble it all yourself, you'll realize why this is such a big deal (Node + ESM + TS is way harder than it should be!). What's more, even though Astro is handling all of that for you, the only code that is specific to Astro is the (ab)use of the frontmatter syntax (---).

In fact, other than the .astro file syntax, most of the Astro-specific things can be covered by three categories: an Astro global object, a few template directives for hydration, and file-based routing. You won't need to spend hours perusing the Astro docs (which are fantastic btw) because Astro tries to defer everything else to the platform (e.g. web APIs) or to other tools (e.g. Vite or React). There is an astro config file but the astro add CLI takes care of most of the things in there.

Bring your own UI framework(s)

When you need more than what is possible inside a .astro template, Astro lets you use components from your framework of choice. This in particular is a breath of fresh air in a world where we see one of two trends: either the author of a UI framework builds their own metaframework to go with it, or the author of a metaframework decides to build it around React.

Astro inverts this idea and goes even further by letting you mix and match frameworks. Imagine using SolidJS as the default for its very solid primitives, utilizing Svelte for its animation utilities, and leveraging React for its ecosystem. All on the same page. What's more, you can even nest frameworks. ๐Ÿคฏ

<div>
  <MySolidComponent client:load>
    <MyReactComponent client:load>
      <MySvelteComponent client:load />
    </MyReactComponent>
  </MySolidComponent>
</div>

Again, this just makes sense. Why reinvent a UI framework when so many excellent options already exist and have different strengths?

Client-side JS is opt-in

Over the course of the last decade, web frameworks have gravitated to a JS-first approach. Astro goes the opposite way by requiring JS to be manually turned on, and it does so quite elegantly, allowing JS to be toggled at a component level rather than page-level.

Ever since Jason Miller's original post on Islands Architecture, I've been curious about actually using it in practice. Previously your options were:

  • ship all of the JavaScript (no islands) or none of it (all land)
  • ditch frameworks and use <script> tags
  • roll your own islands on top of something like 11ty

But Astro is specifically designed around the idea of islands so you get to use components just like you normally would in your favorite framework, and then turn on JS with a template directive. I appreciate the client:idle directive in particular.

<Header /> // <-- this will output static HTML
<LoginButton client:load /> // <-- this will load the JS immediately
<Carousel client:idle /> // <-- this will load the JS via requestIdleCallback

To me, this approach just makes sense. Not everything on the page needs to be hydrated with tons of JavaScript. Think of all the icons that live rent-free in your JavaScript bundle.

(I would be remiss not to mention all the work going on over at Qwik. I'll be giving that a try very soon as well.)

Conclusion

That's all for today. Astro is refreshingly simple and I've been enjoying using it a lot. One quick note: you may have noticed that I refer to Astro as a "metaframework" rather than a "static site generator". This is on purpose because I think Astro does so much more than what you would get from something like Hugo. I plan to elaborate in another article so stay tuned. (edit: maybe some other day)