Recently, I threw out the previous incarnation of my blog, which was originally made with Jekyll, and then again made with Next, and tried again using Astro with the AstroPaper blog template. This is a meta post about rebooting stuff, as is customary whenever anyone replatforms their static website.
Table of Contents
Open Table of Contents
TLDR
The short of it is this: Astro is a well-polished tool that gave me a lot of time back, and that’s why I’m writing up notes about it. I was able to find ways to use it not just for static site generation, but to solve a lot of other potentially complicated things.
The Overview
I needed to evaluate some documentation tools recently, and that led me to a thing called Starlight, which in turn led me to Astro.
- Starlight is a documentation management system that takes care of a lot of the polish that you might expect from current state of the art programming docs. It’s really nice! You do wind up writing and maintaining it all manually, in many cases, but you get sidebars, minimaps, search, SEO, a command palette, shareable pages, etc. Starlight is a template for the static site toolkit, Astro.
- Astro is a static site toolkit. It does the work of assembling a bunch of idioms, sensible defaults, and build / development tooling together into a single well-polished setup. It then superimposes some nice extras on top to make the whole experience very pleasant and yet still customizable.
My learnings here are from reading the docs and re-platforming my tiny single-post blog from Next to Astro. I picked the AstroPaper template, which itself was a great setup, and went from zero in a new repo to deployment on this domain.
All in all, the process took me about an hour or two and then I got my evening back. Which is amazing! I mean, dang.
Good Great docs
Astro seems to use its own Starlight template for its own documentation, which is just outstanding. There’s a lot in there, not just about Astro but its ecosystem as well, and they even took the time to make an Astro tutorial. In fact, if you want to see a great demonstration of Astro in action, go peek at its docs.
The Astro component model
Astro introduces its own component model, in the form of .astro
files, and this turns out to be an easy to grasp, smart choice. An .astro
file is essentially a mash-up of SFCs and JSX.
SFC
SFC stands for “single file components”, and is a design approach popularized by Vue and Svelte. It basically means you split one file into a few different sections. One section is your setup / imports, another is your template, another is your style, and another is your behavior. Then, separately, your tooling chunks those files up into the right part of your program, and does the work of connecting everything for you.
The thing I dig about SFC is that it amplifies the hell out of your ability to refactor and update parts of your program. You can see everything you need to see in, ideally, a single place. Life before SFCs was a lot worse: often, to see a single part of your program, you’d need to have a file open for every one of these slices. SFCs are great.
JSX
I’m not fully aware of what JSX stands for! Which is funny, considering I’ve been using it for about ten years now. Javascript extended? Expressions? I don’t know.
Anyway, JSX is fantastic. It gets a lot of flack but, in my opinion, it’s the core concept that React broke ground on. It’s already surviving React in some obvious ways. Typescript supports it out of the box.
In practice JSX works for much of the same reasons that SFCs work: it disturbs the separation between logic and template, putting them in one spot so you can work on them without dividing your attention or effort. Under the hood, JSX takes things like this:
<div class="toboggan hero">
<h1>Look at my toboggan!</h1>
</div>
And turns it into something a lot like S-Expressions, but as Javascript objects (I’m not displaying the actual transpiled JSX here; that will vary by pragma anyway):
{
tag: "div",
props: {
className: "toboggan hero"
},
children: [
{
tag: "h1",
props: {},
children: [
"Look at my toboggan!"
]
}
]
}
What this means is that if you drop a set of curly braces in JSX, you can dovetail Javascript directly into the resulting objects. Basically, JSX is an s-expression format that lets you dynamically build deeply nested objects. It lets compilers do all sorts of cool stuff.
Mashing them up
Boy, these two technologies blend together well. I mean like, really well. Having the power of immediate Javascript while writing up a SFC instead of leaning on a bunch of domain-specific tags (v-if
and whatnot) is huge. The result of this is that .astro
files are simple to grok and leverage, and Astro needs to do very little education to get a hell of a lot of useful design space for the consumer and the compiler both to leverage.
Astro lets you pick your UI framework
But you might not even need one! The .astro
format does so much work in this regard, and exposes Javascript enough, that it’s possible you can use it for components as much as you like.
However, if you grow past the boundaries of .astro
component capabilities, then Astro will let you pick the framework, or frameworks, of your preference or need. So you can mix Vue components and React components, or dip your toes into a new framework here and there.
The process is pretty straightforward, but not “free”. Each of these frameworks has a runtime, and Astro also needs to know how to build them as static webpages, and so to use a framework, you’ll need an adapter to plug it in.
Thankfully, Astro provides some great tooling to reduce friction here:
astro add react
…And that adds the @astro/react
package to your project, and then configures it for you as well (largely by editing the astro.config.js
file).
As a consumer of Astro, you don’t necessarily need to know much about the tooling / plumbing of the build processes, here, which is such a relief. Just pop it in, and Astro starts working it into the dev server, the hot reloading system, the build process, and the islands system (more on that further on).
There’s packages out there for a bunch of popular frameworks, and the Astro team seems to do a good job at maintaining them.
Islands are fascinating
Astro was my first dive into the island concept, so I’m still a little bit fresh on it all, but it was a nice learning experience. Islands have been popping up frequently enough that I’d become curious, and so far I’m digging the concept a lot.
Essentially, an island is a region of your webpage that is potentially interactive, but and potentially isolated, but not by default. In Astro, an island gets rendered no matter what, but it can become interactive on demand. The default is a static single render.
When you leverage an island, you do it the same way you do any component, except you can add some client-side directives to it. For example:
<TickingNewsChiron client:visible />
These directives inform Astro’s build tooling about how to manage the actual Javascript the island contains. A few of these approaches are:
- No directive. This island is just a static thing.
- client:load - Astro will load the static version of the component, and then it will bundle the interactive version of the component island as well. In environments with Javascript enabled, this component will “come alive” after page load, progressively enhanced.
- client:visible - The same as above, except when the element becomes visible, Astro will load the interactivity.
- …and so on. There’s apparently a decent list of
client:*
directives, and I haven’t used them all yet.
And of course, if it happens that there’s no Javascript environment of note, say your client disabled it, your page still loads.
Astro and some frameworks don’t mesh perfectly
Astro has a big limitation, not as a result of some mistake of Astro’s or anything like that, but just as a result of its static-first design. Basically, since everything has to be renderable without Javascript, you have to do without pervasive access to a good amount of some of the “fancier” framework doodads. Not in every case, of course, but some cases.
The first of these which stood out to me was React’s Context API. This was because I wanted to pop Mantine into a site, and found that because Mantine leverages Context for a lot of features, you couldn’t really fully leverage it in Astro.
The reason why Context won’t work in Astro is sensible: Context represents React’s ongoing runtime. By putting something into React’s context at a higher level of the component hierarchy, you can count on React to expose it to a lower level, without the need to pass it through components to get there.
The workaround
There is a workaround for this, but it’s a consumer-side choice, not a library-compatible one. In other words, it’s there for authors to design with when they make stuff in Astro, but there’s plenty of libraries which just won’t be using it, and so they won’t work as you expect.
The suggested approach is to use nano-stores, which seems fine to me, but I haven’t dug into it much at the time of writing. I haven’t needed to, not for the blog, not yet.
AstroPaper & Astro templates
Instead of kicking off just a minimal install, Astro lets you pick from a bunch of starter templates. It provides a nice searchable index of community templates, and lets you pass the one you pick as an argument to the init command:
pnpm create astro --template=template-repo-name
For this blog, I went with AstroPaper and I’m pretty satisfied with it. It had these features out of the box:
- Pagination, which is actually a lot to engineer from scratch if you don’t get it for free somehow.
- Site maps and RSS feeds. Again, something that can be kind of a pain in the butt if you need to do it yourself. Nothing major, but some work.
- Sensible styling that works in mobile already, with some theming support.
- Dark and light modes.
- A great base component setup.
- Great pre-commit hooks to keep things looking nice and well linted.
- A bunch of great documentation on how to use and customize it.
Generated OG Images
AstroPaper comes with a fantastic kit for generating SVGs dynamically from site pages. I’ve totally adapted mine to look more like my blog color format but this bears special mention for two reasons.
- They might actually mess up some deployments, because generating the images relies on the file system and not every build environment will have that.
- It’s really slick tech regardless, and I was super impressed by it. Essentially it takes your post metadata and writes it out in JSX, then translates that to SVG, and then finally emits a PNG of that SVG. Modern niceties!
It makes me want to explore dynamic image generation from HTML a little more.
Deployment
Astro deployment is, at its core, pretty simple, since it’s a static site generator. Anywhere that you can serve HTML, you can serve an Astro build. That means a bunch of one-click deployments are available out of the box. Stuff like Github Pages, S3 sites, things like that work with virtually no friction.
Astro has a concept of “Endpoints”, which are essentially API routes that you can use to build out server-side behavior in some cases. This, obviously, breaks the static site deployability if you use it. Under the hood, with an Endpoint, you’re describing a full request/response in much the same way you’d outline any given web server route. Github Pages and S3 won’t have any idea what to do there.
Astro manages this complexity by providing adapters, much in the same way it provides plugins and framework adapters.
For example, to prepare your build for deployment on Cloudflare:
astro add cloudflare
This provides build and dev runtimes for your endpoints, and exposes a few Cloudflare specific workflows to you. When your site gets built, this adapter tells Astro how to turn your routes into Cloudflare workers, and how to set up routing that points to them in exactly the way you define in your app. In other words, it turns these endpoints into little serverless functions and manages them for you.
Cloudflare, Netlify, Vercel, and AWS all have adapters for this kind of work. In just about all of those cases, you can give the hosting provider access to your repo and point your DNS at it, and get full TLS and automated deployments / preview deployments, more or less for free.
My writing workflow
Having said all of that, I don’t necessarily like writing posts in VS Code. It’s fine, but it isn’t enjoyable. I like to write in Obsidian. For now, I’m pulling that off with the symlink workflow from this Youtube video.
Conclusion
Overall I’m really happy with Astro, and I’m going to keep it on a short list for future projects. It’s been a great toolkit and, most glowingly of all, it’s made my life a lot easier.