Migrating my website from Next.js to Astro
First of all, why create a blog/portfolio?
My journey building this personal website started with the idea of having a space to share my passion for technology and programming. Although I explain it in detail in the post Hello world 👋🌍, the main reason behind this initiative was to express my ideas, projects, and experiences with others.
And even though there are many platforms and services that make creating blogs and websites easier, I decided to code my own website for multiple reasons. First, I wanted full control over design, functionality, and customization. Coding it from scratch allowed me to adapt it to my specific needs and experiment with new technologies.
Also, as a software developer by profession, I considered building my own website an ideal opportunity to apply and improve my skills. Coding the website myself challenged me to learn and grow throughout the process, and it enriched me professionally.
I think any software developer, especially if they work in web development, should have a digital presentation in portfolio form; and I am convinced that having a blog is a good idea for professional growth. It forces you to reflect on certain technical topics and helps you develop soft skills like written communication.
Why did I choose Next.js initially?
Even though the technology stack should be completely transparent to end users, the first important decision I had to make was which technologies to use to develop the site. After researching and evaluating several options (plain React, Remix, Gatsby…), I chose Next.js for several key reasons.
First, speed and performance were decisive factors in my choice. By default, Next.js pre-renders pages on the server using server-side rendering (SSR) and static site generation (SSG), which guarantees fast load times and a smooth user experience since most of the work does not have to happen client-side and the client receives fully “prepared” HTML.
Search engine optimization (SEO) was also essential in my decision. With Next.js, static page generation is combined with server rendering capabilities, which improves my content visibility in search engines. To clarify, by generating static HTML on the server, Google already knows my website’s page content because it will never change dynamically.
The file structure provided by Next.js also helped me decide to use it: file-system-based routing, API routes inside the same repository, routes with dynamic segments, etc.
The active community, documentation, and ecosystem of Next.js also played a fundamental role in my choice. The availability of plugins, libraries, and online resources made problem-solving and expanding my site’s functionality easier.
Careful, none of these features are exclusive to or invented by Next.js. But it is true that at the moment I was in when I started developing this website, my main focus was React, and Next.js was breaking the mold and crushing it like no other framework.
But then… Astro 1.0 is released
Astro was developed by Fred K. Schott. The idea behind Astro was to simplify web development and reduce the complexity inherent to modern technologies. On August 9, 2022, Fred published the article Astro 1.0 on the Astro blog. This article announced the release of Astro’s first stable version and aimed to break with everything established by Next.js.
Since then, Astro has positioned itself as one of the main alternatives to traditional web development technologies due to its clear focus on shipping less JavaScript to the client by default.
However, I think one of Astro’s main features that, for now, is not possible to find in Next.js is compatibility with any UI library you can imagine. If you don’t want to use any, you can do that, but if you do, you can use React, Svelte, Vue, Lit… alone or mixed, however you want!
To simplify the story a bit, Astro does exactly everything Next.js does, but learning from the mistakes it has:
- Mandatory use of React.
- A lot of boilerplate, even for the simplest applications.
- Until the implementation of App Router and RSC, the way to mix client and server was very cumbersome.
Why did I decide to do the migration?
I hadn’t felt the same way using Next.js for a while, and I never updated to version 13 (which includes App Router and RSC) because of the complexity that implied. Honestly, it was much easier for me to migrate the entire website to Astro than to migrate Next.js from version 12 to 13…
Leaving that topic aside, which has more to do with lack of motivation than anything else, I decided to migrate because every time I used Astro for small projects (like Simple Weather App) I could prototype new features extremely fast, using Vite as compiler is crucial for me, and of course being able to use JSX inside .astro files without needing to load React on every page.
I have to say that, as much as I like Next.js, Astro has become one of the first technologies I reach for in almost any type of personal project.
Although not everything is perfect with Astro…
Astro is great and I love it, but not everything has been as easy as it could have been. One of the things that frustrates me most about Astro is not being able to use server code (.astro) inside client code (.tsx with React). Astro components cannot be used inside UI framework components unless you do hacks like using <slot> and the children prop.
That said, I understand the limitation and I must say that in Next.js something similar happens with React Server Components.
Now yes, what does Astro clearly do better than Next.js?
- Content Collections: CMS, for what? In version 2.0, Astro introduced Content Collections. A super simple way to manage static content like, surprise, blog posts (or portfolio projects).
- Integrations: Astro’s ecosystem has improved greatly in recent months, and it already has more than 300 integrations (official and community) that greatly simplify SEO, Analytics, deployments, and almost anything you can think of.
- Zero JavaScript: By default, Astro sends pages to the client with zero JavaScript. This means they are not interactive, but since it has Astro Islands, this is not a problem because those minimal parts that do need interactivity are hydrated with their corresponding JS without affecting the rest of the page.
Code comparison
In this comparison, which is shocking at first glance (because it really is), you can see how in Next.js I manually handle blog post data. I have a getArticle function that I use to retrieve frontmatter and post content.
In Astro, however, I use Content Collections and predefined methods like getEntryBySlug. I only have to delegate this work to Astro and it handles everything by itself.
Careful, I’m sure there are a thousand different ways to approach this situation, and almost three years passed between when I developed each version, but this helps us get an idea of how much Astro simplifies development.
export const getStaticProps: GetStaticProps = async ({ params }) => {
const post = getArticle(ArticleTypes.POSTS, params?.slug as string);
const [previous, next] = getPreviousNext(
ArticleTypes.POSTS,
params?.slug as string,
);
const mdxSource = await serialize(post.content, {
scope: post.data,
mdxOptions: {
remarkPlugins: [
RemarkAutolinkHeadings,
RemarkSlug,
RemarkCodeTitles as any,
],
rehypePlugins: [mdxPrism],
},
});
return {
props: {
source: mdxSource,
post,
previous,
next,
},
};
};const post = await getEntryBySlug("posts", slug!);
let previousPost: CollectionEntry<"posts"> | undefined;
if (previous) previousPost = await getEntryBySlug("posts", previous);
let nextPost: CollectionEntry<"posts"> | undefined;
if (next) nextPost = await getEntryBySlug("posts", next);
const {
remarkPluginFrontmatter: { readingTime },
Content,
} = await post!.render();Performance comparison
I ran some tests between production versions of both website versions. The results might surprise you as much as they surprised me:
| Metric | Next.js | Astro | Approx. improvement |
|---|---|---|---|
| Average duration of 10 deployments | 01:42 | 00:42 | 50% |
| Initial JavaScript load | 1.4MB | 217kB | 84% |
| First Contentful Paint | 2.8 s | 1.4 s | 50% |
| Total Blocking Time | 8300 ms | 0 ms | 100% |
| Speed Index | 5.2 s | 1.4 s | 73% |
| Largest Contentful Paint | 3.0 s | 2.7 s | 10% |
| Metric | Next.js | Astro | Approx. improvement |
So, what now?
One of the next steps I’d like to take is implementing Bun now that the first stable version has been released. It’s still unclear whether I’ll make this change, because at the time I’m writing this post, Bun still has many things to polish and still has a long way to go to settle in the market.
This would mean replacing Vite and PNPM with Bun, achieving lightning-fast speeds during development and deployment (build process).
In conclusion…
I think I sold Astro very well in this post, but I genuinely like it. I think it’s an alternative that, in a very short time, has known how to do things properly while listening very closely to the web development community.
I recommend giving Astro a chance if you are thinking about building a static website or one with little interactivity and, of course, I also recommend trying Next.js if you haven’t yet, since it will give you huge knowledge about React and Server Components.