Moving to Astro from React and Next.js

The cosmos
Photo by Dario Brönnimann on Unsplash

As a holiday project, I decided to rebuild my personal website. Like many others, I’ve been a bit disenchanted with the developments around next.js and Vercel lately. So I started looking around for other solutions. Remix looks amazing but is a bit overblown for a simple blog like this humble website is—even though I really want to try it on the next bigger project. I heard good things about Astro and decided to give it a try. You are reading this on the relaunched blog running on Astro.

Here are some things I liked and disliked about Astro, coming from React and next.js.

What I liked

1. It’s static-first by design

I like that by default, astro build just spits out a dist folder with static files. No matter how often Vercel and remix alike try to make me believe that running a web service at the edge is the easiest way to do things, I still like my static files for a simple website. I think a folder of files is the most performant, robust and portable way to host a website. Push-to-deploy to a CDN is really easy to set up these days, so that‘s no good reason to use Vercel.

2. Easy markdown integration

Markdown with integrated syntax highlighting, and more so MDX, work out of the box (well not quite, but with an integration that takes one command to set up). When I set this up with next.js a while ago, there were a myriad of ways of doing it, that involved fiddling around with file system APIs, manual slug generation and metadata setup.

3. It’s mostly HTML

Do you really need React? For a lot of websites running on it, I think the answer is actually no. However, I don’t want to miss out on how nice it is to develop sites with the component model. This is where Astro shines—you can create components and import them like in React, but they don’t ship any JS to the client by default. Kind of like React Server Components, but without all the mumbo jumbo that comes with them.

4. It’s easy to make it fast

The nice thing about next.js is the automatic prefetching they do with the <Link> component to make page navigations feel instant. Turns out you can have the same thing in Astro: By enabling the prefetchAll option in the config.

What I disliked

1. The slot system

When passing HTML content to components, Astro is using slots. You may have seen this before in web components. While I like that they adhere to web standards, I think passing JSX as props is superior to the slot system. Only having one unnamed <slot /> (the equivalent of React children) is easy and makes sense, but with multiple named slots things get confusing. Looking at an example from their website:

<Wrapper title="Fred's Page">
  <img src="https://my.photo/fred.jpg" slot="after-header">
  <h2>All about Fred</h2>
  <p>Here is some stuff about Fred.</p>
  <p slot="after-footer">Copyright 2022</p>
</Wrapper>

At a glance, it looks like the image and paragraph are being rendered at the top, and, respectively, the bottom of the element. However, depending on how the <Wrapper> component renders things, they could also be rendered the other way around, or not at all.

The problem here is that looking at the template code in HTML form, it’s already implying a rendering order, since this is what we know from HTML.

Let’s look at the React equivalent:

<Wrapper
  title="Fred's Page"
  image={<img src="https://my.photo/fred.jpg" />}
  copyRight={<p>Copyright 2022</p>}
>
  <h2>All about Fred</h2>
  <p>Here is some stuff about Fred.</p>
</Wrapper>

The content here is passed as props. Since props behave somewhat like HTML attributes, we have already learned that their order doesn‘t matter. So intuitively, this doesn’t imply a rendering order, like the Astro example does.

2. No way to locally define a component

In React, and especially when working with tailwind, it’s often helpful to create small helper components in the same file as the main component, to avoid duplicating tailwind class strings. At a later point, I might break out the component into its own file, if it’s being required in other places.

AFAIK, this is not possible in Astro, every component has to have its own file from the start, which is a bit tedious sometimes.

3. It’s more error-tolerant

Turns out I actually dislike this. JSX will not compile if a tag doesn’t have a closing equivalent. An astro template will, however—and leave it up to the browser how to deal with the broken markup. During development this has actually caused more problems than it solved. Strictness is sometimes better.

4. No first-class vim integration

Most won’t care about this, but not everyone is using VS Code. There is a community made plugin for vim, but it’s only made by one person, so it’s not too feature rich. Confusingly, there is a project called AstroNvim, which has nothing to do with Astro the build system.

Conclusion

For content-driven websites that don’t require a lot of interactivity, I’ll definitely use Astro again. I realize that Astro has islands where I can use React components, but I feel like if I’m going to ship the whole React bundle, I might as well use something like remix or next.js, so I don’t have to mentally switch between Astro’s and React’s component model.