logo
Published on

Migrating my Portfolio to Astro

Authors

banner

Introduction:

So I recently finished upgrading my portfolio to Next.js 13 and its new app directory. However, after finishing the upgrade I decided to migrate everything to Astro, as it is way better than Next.js in this case. So I basically had to go through the pain of redoing my portfolio twice, however, in the end it was totally worth it.

Why I Chose Astro:

The decision to migrate to Astro came to me after carefully evaluating various options. Astro’s reputation for blazingly fast performance, ease of use, and alignment with my project’s specific requirements were the key factors that convinced me to make the switch, as Astro’s main focus is on static sites, like in this case, even though you can create dinamic ones if you want.

astro

The Upgrade Process:

I’m not going to lie and pretend that the upgrade process was a walk in the park, because it wasn’t. It was extremely tedious to to migrate to the new .astro file that Astro mainly uses, and I know that you can use React as well, but I wanted to learn something new as I had already done a full project with react - Kyodo - which you can check out on the projects page by the way.

So yeah, It was pretty tedious, even more if you take into account that just about some days ago I had just finished migrating from Next.js 12 to next.js 13, having to rewrite most of the project for it to work with the new app directory.

Just to see the difference between React and Astro, this is an example of a script of my portfolio written in React and the same script written in Astro.

React

layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html
      lang={siteMetadata.language}
      className={`${space_grotesk.variable} scroll-smooth`}
      suppressHydrationWarning
    >
      <link rel="apple-touch-icon" sizes="76x76" href="/static/favicons/apple-touch-icon.png" />
      <link rel="icon" type="image/png" sizes="32x32" href="/static/favicons/favicon-32x32.png" />
      <link rel="icon" type="image/png" sizes="16x16" href="/static/favicons/favicon-16x16.png" />
      <link rel="manifest" href="/static/favicons/site.webmanifest" />
      <link rel="mask-icon" href="/static/favicons/safari-pinned-tab.svg" color="#5bbad5" />
      <meta name="msapplication-TileColor" content="#000000" />
      <meta name="theme-color" media="(prefers-color-scheme: light)" content="#fff" />
      <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#000" />
      <link rel="alternate" type="application/rss+xml" href="/feed.xml" />
      <body className="bg-white text-black antialiased dark:bg-gray-950 dark:text-white">
        <ThemeProviders>
          <Analytics analyticsConfig={siteMetadata.analytics as AnalyticsConfig} />
          <SectionContainer>
            <div className="flex h-screen flex-col justify-between font-sans">
              <SearchProvider searchConfig={siteMetadata.search as SearchConfig}>
                <Header />
                <main className="mb-auto">{children}</main>
              </SearchProvider>
              <Footer />
            </div>
          </SectionContainer>
        </ThemeProviders>
      </body>
    </html>
  )
}

Astro

MainLayout.astro
---
import SectionContainer from "@/components/SectionContainer.astro";
import siteMetadata from "@/data/siteMetadata";
import Link from "@/components/Link.astro";
import Header from '@/components/Header.astro';
import Footer from "@/components/Footer.astro";

//css
import "@/styles/prism.css";
import "@/styles/tailwind.css";
import "@fontsource-variable/inter";
import "katex/dist/katex.css";
import "@/styles/global.css";

interface Props {
    title?: string;
    author?: string;
    description?: string;
    ogImage?: string;
    canonicalURL?: string;
}

const {
    title = siteMetadata.title,
    description = siteMetadata.description,
    author = siteMetadata.author,
    ogImage = siteMetadata.socialBanner,
    canonicalURL = new URL(Astro.url.pathname, Astro.site).href,
} = Astro.props as Props;

const socialImageURL = new URL(
    ogImage ? ogImage : siteMetadata.socialBanner,
    Astro.url.origin
).href;
---

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width" />
        <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
        <link rel="canonical" href={canonicalURL} />
        <meta name="generator" content={Astro.generator} />

        <!-- General Meta Tags -->
        <title>{title}</title>
        <meta name="title" content={title} />
        <meta name="description" content={description} />
        <meta name="author" content={author} />
        <link rel="sitemap" href="/sitemap-index.xml" />

        <!-- Open Graph / Facebook -->
        <meta property="og:title" content={title} />
        <meta property="og:description" content={description} />
        <meta property="og:url" content={canonicalURL} />
        <meta property="og:image" content={socialImageURL} />

        <!-- Twitter -->
        <meta property="twitter:card" content="summary_large_image" />
        <meta property="twitter:url" content={canonicalURL} />
        <meta property="twitter:title" content={title} />
        <meta property="twitter:description" content={description} />
        <meta property="twitter:image" content={socialImageURL} />

        <!--Theme -->
        <meta name="msapplication-TileColor" content="#000000" />
        <meta name="theme-color" content="#000000" />
    </head>
    <body class="dark:bg-neutral-900">
        <SectionContainer>
            <div class="flex h-screen flex-col justify-between">
                <Header />
                <main class="mb-auto"><slot /></main>
                <Footer />
            </div>
        </SectionContainer>
    </body>
</html>

I think that the Astro language is easier to work with and understand, as well as being much more simpler than React. It’s like plain HTML but with steroids. And you can bring your favorite UI framework if you want to, Vue, Svelte, React, etc. All of them are compatible in Astro.

Performance Gains:

One of the most rewarding aspects of this transition has been the remarkable boost in performance. By switching to Astro, the load times of the whole blog have been significantly reduced, enhancing the overall user experience. The instant rendering capabilities of Astro ensures that visitors can access content exceptionally quickly.

Here are some reports of Lighthouse of some routes, keep in mind that the page is not completely optimized and the results of the final page will be greatly improved.

1. Main page

ligthhousereport

2. A blog post page

ligthhousereport

3. The blogs page

ligthhousereport

4. The tags page

ligthhousereport

So Astro’s claims of having a blazingly fast performance are true and here is the proof of it.

Conclusion:

Looking back on this journey, the decision to transition from Next.js to Astro feels incredibly rewarding. The benefits I currenly have in terms of speed, ease of use, and of course having learned something have been fulfilled beyond my expectations. My portfolio and blog now stand as a testament to the power of trying new technologies and methodologies, even if they are not the most popular ones.

Something which I haven’t mentioned is that Astro has native support for markdown files, so you can basically customize the styles of them and create entire pages with it. In my case I use them to create the blog posts, I have a Layout that renders everything (navbar, styling…) and the actual content is written in a markdown file. This is also very convenient because you don’t have to mess with code when creating a new post or modifying one.

So for anyone who is in the same spot as me at the beginning of the post, or simply wants to creates its own portfolio or blog, Astro is one of the, if not the best option out there. There are many other benefits in Astro, such as image optimization out of the box, amazing content system… But you will discover them by yourself when creating your own site.