From NextJS to Astro

6 min read

In 2023 I began making the move from NextJS to Astro for my personal website and blog. This is my experience with the transition.

Moving from NextJS to Astro.Moving from NextJS to Astro.


I have been using NextJS since the v3 iteration and the last iteration of my website was written using v12. I had mostly been very impressed and happy with using Next. That started to change with v12 as I began to feel more and more like it was doing too much. I still think it’s the go too at least at the time of writing for anyone wanting to create a full-stack application with React (albeit Remix is hot on Next’s tail!). There are other options today as well including but not limited too Nuxt, SvelteKit, and QwikCity. For this iteration though I opted to give the new hotness on the block Astro a spin and I got to say I love using Astro.

Why move away from NextJS?

I would say the number one reason was that when something goes wrong in Next or you get off the beaten path of the examples directory…you are in for some real fun with the error messages! Once you get the hang of the error messages that are pertaining to when you are on the server or the client it becomes a little bit easier, but with the advent of some of the newer features of Next there is a whole other layer of complexity. I don’t know about you, but it definitely seems like since mid-2023 we as an industry are moving back to server-side paradigms and throwing out all the complexity we can.


Being someone who got started in 2015 learning React and web development in the initial days of rolling your own webpack, babel, tsconig, etc etc etc I like many have some serious “config fatigue”.

What is so great about Astro?

There is a lot to love about Astro. My favorite features have to be Astro Islands, Frontend Framework Integrations, and Content Collections. But it’s worthy to note other features that make Astro great!

  • ease of getting started without a ton of configuration necessary
  • by default Astro is server-side rendered (SSR)
  • by default Astro will send zero JavaScript to the client
  • ease of customization through official and community plugins

Astro Island Architecture

An island, as per the Astro docs, is an interactive UI component. On this website, for example, when viewed on mobile their is a hamburger menu that opens to a dropdown and transitions into the viewport using @headlessui/react. Since this is using React code when utilizing the component I need to pass a client side directive to the component telling Astro when/how to load the component.


If the client:only='react' directive is not passed then Astro will strip out the client side JavaScript to prevent sending unnecessary or unwanted JavaScript to the client. This is the default behavior of Astro as it is server-side rendered and one of it’s goals is to ship the least amount of JavaScript to your users. The same concept is used with the <ThemeToggle client:only='react' /> that is triggering global JavaScript code to toggle the theme and persist that change in localStorage.

Frontend Framework Integrations

Astro can easily work with nearly all the major frontend frameworks like AlpineJS, AngularJS, Lit, PreactJS, Qwik ReactJS, SolidJS, Svelte, VueJS and even the new hotness htmx

In my case for migrating away from NextJS it was pretty simple to just bring my React code right over with minimal adjustments needed. I won’t go into how to do this as you can follow the guide for adding React and another guide for integrating TailwindCSS through the Astro docs.

Content Collections

This is a real godsend for managing content within the blog portion of my website. zod enables a type-safe schema that will let me know right away if I missed something in the frontmatter of a post, if the type is not correct, or I am using a tag that has not been defined yet within the blog. It’s a great addition and has made managing my blog significantly easier than past iterations using Gatsby & Next.

import { defineCollection, z } from 'astro:content'
const blogSchema = z.object({
	createdAt: z.string().transform(str => new Date(str)),
	description: z.string(),
	draft: z.boolean().default(true),
	title: z.string(),
export const collections = {
	// Astro will now expect to find content at
	// ~/content/blog/
	blog: defineCollection({
		schema: blogSchema,
		type: 'content',
Defining the schema for a blog post with Zod.

With the schema defined the next time the Astro development server is started a hidden directory called .astro/ will be generated with a types.d.ts that conforms to the schema you defined. Now when Astro is loading you markdown or MDX files at runtime if any of the required keys of the schema are missing or of the incorrect type you will see error messages in the terminal and browser to tell you the contract you defined is not being met. It’s become super handy to have that schema in place as I am working to port over older posts and archive them. I can copy and past an old post in and then let the failing contract point me to all the things that need to be updated within that post.

You can take a look at how I manage content using content collections here.

My experience with the migration

This has been probably one of the easiest migrations from one piece of tech to another I have ever done. The fact I could drag and drop the necessary React components into this and it just work was the chef’s kiss. TailwindCSS integrated with zero issues as well. I really have enjoyed using the Astro components and getting back to just working primarily with HTML and lowering the amount of complexity around components and the code base in general. One thing I am really hoping to figure out is how to extract out the general theme and components into my own library to use across the various projects. I say this because is it’s own Astro application as is They both run on their own instances. There are other projects that are on the way that will live under different subdomains as well so being able to just import the theme and necessary global components would be fantastic!

Next steps

  • Replace React code with AlpineJS and htmx.
  • Abstract out tailwind config to use across subdomains.
  • Abstract out global components into a library to use across subdomains instead of relying on copy pasta.
  • Move away from a completely statically generated site to a hybrid model.
  • Add i18n to the website and eventually the blog covering both English & Spanish languages.
  • Add a custom endpoint for handling the addition of a newsletter.
  • Add realtime views & likes with PocketBase.

Wrap Up

Overall I have been very impressed and enjoy using Astro as my go to framework now when developing JavaScript application. I am sure in two to three years there will be something new that comes along…we are talking about the JavaScript ecosystem after all. For now though Astro is my choice because as Brian Fantana would say:

~ Cody 🚀

Related Articles

    Going to the Gopher Side

    The chaos that is the JavaScript/TypeScript ecosystem has become too much to bear in my opinion. I have become unhappy with my direction in the tech industry and in late 2023 made the decision to begin teaching myself Go and pivoting my career out of the Frontend & away from JavaScript.

    Adding Related Articles with Astro Content Collections

    Astro's content collection feature is a game changer for easily managing content in a type safe manner. It also can help to easily add related collections or data types through referencing. In this article I show how I implemented a related articles feature.

Cody Brunner

Cody is a Christian, USN Veteran, Jayhawk, and an American expat living outside of Bogotá, Colombia where he works as a Senior Frontend Developer for Bitcoin IRA.