Why and how I moved my blog away from Gatsby and React to Astro Js and Preact

Why and how I moved my blog away from Gatsby and React to Astro Js and Preact

Astro is primarily a static site generator. It allows you to structure your site however you want and generate HTML pages only once, using components.

Imagine that you only want to focus on writing your blog and generate static content. After researching online, you might come across recommendations to learn React, Vue, Angular, or some other new stack that you’re not familiar with. Even worse, someone might suggest using Wordpress.

Sometimes, you don’t need to make your life more difficult if all you need is to generate HTML sites.

As a developer, there will be times when I need to generate certain components dynamically. For example, I might want to obtain and display the two latest posts from my blog.

My previous blog was built with GatsbyJS and was also migrated from Wordpress.

Initially, creating static pages with Gatsby was fine. However, as I wanted to develop advanced features while maintaining the same structure, it became complex. Additionally, its API for retrieving posts had nested objects, which I refer to as the “node hell”.

The main reason behind it

If I were to choose a compelling reason to choose Astro, rather than any other framework like NextJS, which is very popular these days, it would be because I want to write simple HTML for my components or pages and have the opportunity to write articles using Markdown, a widely used language among developers. You don’t have to worry about clicking between different buttons or using shortcuts to apply formatting to the text, like in Word.

All is about simplicity and power at the same time. When the libraries provide us with more features, I will spend more time writing articles instead of fixing parts of my blog that are not working.

Take into consideration that Astro uses its own file format: .astro. Even though it’s a new format, you will get used to it quickly because it resembles HTML with some modifications. You can write JavaScript or TypeScript within the same file, and it will be processed by the bundler anyway.

Additional comments

Avoid using GraphQL for a simple blog

Gatsby has a strong dependency on GraphQL, probably because it was popular when Gatsby was rising in the market, around 2017. Its queries were used to retrieve and work over data structures that came with the framework.

For example, here is a comparison to retrieve all posts by language in Gatsby.

gatsby-node.ts
const createTagsPage = async (graphql: CreatePagesArgs['graphql'], createPage: Actions['createPage']) => {
const tagTemplate = path.resolve('./src/components/Templates/PostsByTagAndLocale.tsx');
for (const locale of allLanguages) {
const result = await graphql<Queries.AllTagsByLocaleQuery>(
`
query AllTagsByLocale($locale: String!) {
tagsGroup: allMdx(filter: { fields: { locale: { eq: $locale } } }) {
group(field: { frontmatter: { tags: SELECT } }) {
fieldValue
}
}
}
`,
{ locale: locale },
);
const tags = result.data?.tagsGroup.group;
for (const tag of tags) {
createPage({
path: localizeUrl(`/tags/${tag.fieldValue}`, locale),
component: tagTemplate,
context: {
tag: tag.fieldValue,
locale,
tagsForSeo: tags,
},
});
}
}

We can observe the dependency on GraphQL. To test the queries, you had to use its tool GraphiQL, and build them there. In Astro, we only rely on JavaScript or TypeScript.

utils.ts
export const getPostsBy = async ({
language,
limit,
}: {
language: "en" | "es" | "fr" | "pt";
limit?: boolean;
}): Promise<CollectionEntry<"blog">[]> => {
let posts = await getCollection("blog", ({ slug }) => slug.startsWith(`${language}/`));
posts = posts.sort((current, next) => next.data.pubDate.valueOf() - current.data.pubDate.valueOf());
if (limit) {
posts = posts.slice(0, 2);
}
return posts;
};

Since Astro generates the types when the server is up, we have autocomplete by default. Gatsby always generates them, but with the caveat that it takes more time to turn on the development server.

Support for TypeScript

Gatsby had strong support for TypeScript, but as soon as you wanted to deviate from the Gatsby way of doing things, it would become difficult to have typing for your Nodes. Astro, even in its own type of files, allows you to use TypeScript.

How strict do you want your configuration? It’s totally up to you. Astro gives you the option to choose how strict you want your typing to be.

Here is an example showing how, for a button component, I can specify its types inside an .astro file.

---
import { icon } from "@fortawesome/fontawesome-svg-core";
import { faAppStore, faGooglePlay } from "@fortawesome/free-brands-svg-icons";
const faAppStoreIcon = icon(faAppStore).html[0] as string;
const faGooglePlayIcon = icon(faGooglePlay).html[0] as string;
type Props = {
title: string;
link: string;
isAppleApp: boolean;
};
const { link, title, isAppleApp } = Astro.props;
---
<a
href={link}
target="_blank"
class="inline-flex rounded-full px-5 py-3 items-center mr-2 bg-black text-white dark:bg-gray-700 dark:text-white"
>
<Fragment set:html={isAppleApp ? faAppStoreIcon : faGooglePlayIcon} />
<span class="ml-3 font-semibold font-quicksand">{title}</span>
</a>

To read more about TypeScript setup for Astro, check this link .

Faster development and deployment times

I remember every time I wanted to work in my Gatsby’s local environment. It took more than a minute from the moment I executed the command, and for less than 40 pages, it was completely pointless. What if I wanted to add a new NPM package and restart the server? What if I left unfinished code and the editor autosaves it, but my local server fails and stops? There we go, another minute wasted trying to turn it on just to keep writing or coding.

Furthermore, Gatsby had longer build times. My latest Vercel deployments showed an average time of 2 minutes. Even worse, Gatsby offered “incremental deployments,” an additional service on their hosting site that allowed you to only build the modified files, instead of fixing its build times. It was a complete disappointment.

With Astro, generating around 40 pages when you start the server is almost instant, taking less than 10 seconds. For deployments, it takes half the time, around 50 seconds as of November 2023.

Compatibility with different frameworks

If you still have plugins that only work in specific frameworks like React, Astro gives you the opportunity to continue using them. Therefore, you can retain certain features that you don’t want to code from scratch in Astro.

In general, I would recommend not using any frontend framework unless necessary, as it adds some weight to your final JS file size, which can increase load times. The objective of Astro is to maintain a lightweight execution.

Excellent documentation

Astro, just like Laravel, my favorite framework, has clear documentation that shows the most common tutorials when creating a static site. It provides features such as adding code syntax to your posts, support for multilanguage, enabling MDX in your publications, and more.

Easy modification of SEO tags

Every Astro component uses HTML syntax. You can use all types of HTML tags, even <html>. If you want to include anything in your <header>, such as SEO tags, page title, and similar elements, it is easier to write them without using additional components. However, for more flexibility, it is much better to extract your <header> as a component.

Easy hosting support

Astro, when generating HTML, is compatible with hundreds of services in the market, including some free ones. I chose Vercel because it offers branch and commit deployment.

My experience migrating frameworks

It’s worth noting that I didn’t start migrating my site from Gatsby to Astro from day one. What I wanted to do was to check how many similarities there were or how difficult it would be to implement some of the features that my Gatsby blog had.

The truth is that migrating to Astro wasn’t as difficult as I thought initially. Astro comes with many tools, and I followed the “how to create a blog” tutorial. My experience using Frontmatter also helped a lot because I didn’t have to make drastic changes to its configuration.

These are the numbered steps that I took to migrate to Astro.

Steps to verify the migration of my Gatsby blog to Astro

  1. Install the project based on the blog template. It comes with some pre-built features, such as generating multiple pages by a slug.
  2. Add Tailwind, which also has support from Astro, by following a few steps.
  3. Set up Frontmatter and enable MDX.
  4. Create a file and page structure to support multiple languages, with URL prefixes starting with a specific language. For example, /blog and /es/blog.
  5. Retrieve blog posts by language. For this, each of my files has the same file name, but they are located in different language folders.
  6. Verify that you can add support for a light and dark theme. I achieved this by combining JavaScript’s localStorage with an Astro component.
  7. Ensure that on my main page, I can display the latest two posts ordered by their creation date.
  8. Assign a group of tags (or hashtags) to each post, then retrieve all the tags and generate a new page for each tag. This new page will list all the posts in a specific language that contain the respective tag.
  9. I double-checked that the URL slugs from posts remain the same as before to avoid losing indexing from Google.
  10. Create a client-side search bar that allows me to find my posts by title or description.
  11. While working on the above steps, I was also migrating the entire UI, including classes and styles.
  12. Migrate from React and optimize it with Preact.
  13. Improve SEO by enhancing titles, adding missing tags, and fixing titles and descriptions.
  14. Deploy to Vercel and perform initial tests. Once I had a preview with 80% of the features from my old blog, I pushed it to production.

Some difficulties arose

Below are some of the features that took me the most time to find a solution for when I was migrating to Astro.

Migrating FontAwesome

Migrating the icons from FontAwesome was one of the most challenging tasks because there are compatibility gaps between Astro and the SVG icons.

When using Gatsby or any other React-based framework, you need to import the @fortawesome/react-fontawesome library. However, with Astro, this is not necessary.

Terminal window
# Install core library
npm i --save @fortawesome/fontawesome-svg-core
# Install free icons
npm i --save @fortawesome/free-solid-svg-icons
npm i --save @fortawesome/free-regular-svg-icons
npm i --save @fortawesome/free-brands-svg-icons

Inside your Astro component, import only the icons that you need. I’m going to include the code as I did for this site footer.

---
import { icon } from "@fortawesome/fontawesome-svg-core";
import { faGithub, faLinkedin } from "@fortawesome/free-brands-svg-icons";
---

Next, use the icon function from core to generate the SVG.

---
import { icon } from "@fortawesome/fontawesome-svg-core";
import { faGithub, faLinkedin } from "@fortawesome/free-brands-svg-icons";
const faLinkedinIcon = icon(faLinkedin).html[0];
const faGithubIcon = icon(faGithub).html[0];
---

Once the generated SVGs from those icons are stored in our variable, combine them with the Fragment component from Astro. This component doesn’t need to be imported with the same name as React does. And pass it to its set:html property.

---
import { icon } from "@fortawesome/fontawesome-svg-core";
import { faGithub, faLinkedin } from "@fortawesome/free-brands-svg-icons";
const faLinkedinIcon = icon(faLinkedin).html[0];
const faGithubIcon = icon(faGithub).html[0];
---
<footer class="bg-black dark:bg-gray-800">
<div class="container flex flex-col items-center py-6 mx-auto">
<Fragment set:html={faLinkedinIcon} />
<Fragment set:html={faGithubIcon} />
</div>
</footer>

The icon will render with no issues at all now. If you want to add some styling, place the Fragment tag inside another HTML component and proceed to add the classes or styles there.

Astro components vs React components

The Astro components are very similar to the React ones. For the migration, I had to learn how to pass properties and maintain their types. Here is an example of my Astro component, which is a button that changes its icon depending on the boolean value that you send to it.

StoreButton.astro
---
import { icon } from "@fortawesome/fontawesome-svg-core";
import { faAppStore, faGooglePlay } from "@fortawesome/free-brands-svg-icons";
const faAppStoreIcon = icon(faAppStore).html[0] as string;
const faGooglePlayIcon = icon(faGooglePlay).html[0] as string;
type Props = {
title: string;
link: string;
isAppleApp: boolean;
};
const { link, title, isAppleApp } = Astro.props;
---
<a
href={link}
target="_blank"
class="inline-flex rounded-full px-5 py-3 items-center mr-2 bg-black text-white dark:bg-gray-700 dark:text-white"
>
<Fragment set:html={isAppleApp ? faAppStoreIcon : faGooglePlayIcon} />
<span class="ml-3 font-semibold font-quicksand">{title}</span>
</a>

Notice how I specify that the component requires 3 properties: const { link, title, isAppleApp } = Astro.props;

Astro does not need to be imported, as it is globally available.

To ensure strong typing, we have the Props type. We assign the type to the variables that we specified in the Astro.props line.

type Props = {
title: string;
link: string;
isAppleApp: boolean;
};

React plugins and using Preact

Due to some components that I still have on my site, that was one of the reasons why I wanted to continue using React. However, there is a smaller version that has a high chance of still executing your React plugins. We are talking about Preact, a minimalist framework that is based on React syntax and prides itself on having only a 3kb size.

To learn more about Preact, check out this site .

Preact integrates with Astro in just a few steps, as you can read in this link , and it also comes with compat support. This is what I enabled to continue using my Typing animation library that appears on my homepage.

We need to be aware that for Preact typing, the types are not the same as React. Here is a demo of my search bar, which was coded in Preact.

search.tsx
const SearchBar = (props: {
searchQuery: string;
setSearchQuery: (value: string) => void;
typeAndPressEnterText: string;
}) => {
const handleInputChange = (event: Event) => {
props.setSearchQuery((event.target as HTMLInputElement).value);
};
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Enter") {
props.setSearchQuery(props.searchQuery);
}
};
return (
<section className="w-full mt-9">
<input
id="search-input"
type="text"
placeholder={props.typeAndPressEnterText}
className="w-full py-5 px-4 text-2xl bg-white border-2 rounded-md shadow-sm dark:bg-gray-800 dark:border-gray-900 caret-blue-200 dark:text-white font-quicksand"
onInput={handleInputChange}
onKeyDown={handleKeyDown}
value={props.searchQuery}
/>
</section>
);
};

Look at the highlighted parts of the code; those will be different if you compare them to React. Also, we see that instead of using onChange, we use onInput because this is a recommended implementation from Preact itself.

Support for light and dark themes using Tailwind and Astro

In my previous blog on Gatsby, I had to add support for switching between day and night mode. The trouble was that all of that functionality used a lot of React contexts. So, when I migrated many of my components from React to Astro, I was considering an alternative solution. Additionally, I read about how Tailwind has easy support for this.

Here, you can read how I ended up coding my component, which uses the localStorage from the browser to store user preferences.

ThemeToggle.astro
<section id="themetoggle" class="hover:cursor-pointer text-xl">
<span id="themetoggle_moon" class="hidden text-black"> </span>
<span id="themetoggle_sun" class="hidden text-white"> </span>
</section>
<script>
import { icon } from "@fortawesome/fontawesome-svg-core";
import { faMoon, faSun } from "@helmerdavila/fontawesomehelmer/standalone/pro-duotone-svg-icons";
const loadTheme = () => {
const theme = localStorage.getItem("theme") ?? "light";
// Hide or show sun or moon icon depending on the current theme
if (theme === "dark") {
document.documentElement.classList.add("dark");
document.querySelector("#themetoggle_sun")?.classList.remove("hidden");
} else {
document.documentElement.classList.remove("dark");
document.querySelector("#themetoggle_moon")?.classList.remove("hidden");
}
};
const loadIcons = () => {
// Using load icons from FontAwesome
const faMoonIcon = icon(faMoon).html[0] as string;
const faSunIcon = icon(faSun).html[0] as string;
const spanIconMoon = document.getElementById("themetoggle_moon") as HTMLElement;
const spanIconSun = document.getElementById("themetoggle_sun") as HTMLElement;
spanIconMoon.innerHTML = faMoonIcon;
spanIconSun.innerHTML = faSunIcon;
};
// Add event of changing theme when user clicks section with id themetoggle
document.getElementById("themetoggle")?.addEventListener("click", () => {
const theme = localStorage.getItem("theme") ?? "light";
const nowUseTheme = theme === "light" ? "dark" : "light";
localStorage.setItem("theme", nowUseTheme);
window.location.reload();
});
// When dom is loaded, load theme and icons
window.addEventListener("load", () => {
loadTheme();
loadIcons();
});
</script>

Conclusions

At the moment of writing this post, my blog has been using Astro for two months. I think that I have never felt so comfortable and productive while working on a web page creation tool with clear documentation that quickly starts my local development server. Additionally, I appreciate that adding SEO support is very easy and all the information about it is documented on its official site, eliminating the need to double-check with other tutorial sources or browse through Github comments to find solutions to common problems that developers may encounter when working with these types of frameworks.

My posts are not AI generated, they might be only AI corrected. The first draft is always my creation

Tags

Author

Written by Helmer Davila

In other languages

Astro es un generador de sitios estáticos principalmente. Ordena la estructura de cómo quieres que tus páginas se generen en HTML una vez, usando componentes, y dedícate después a crear contenido.

Porqué y cómo moví mi blog de Gatsby con React a Astro JS y Preact

Astro est principalement un générateur de sites statiques. Il organise la structure de la façon dont tes pages sont générées en HTML une fois, en utilisant des composants, et il se charge ensuite de la création de contenu.

J’ai migré mon blog de Gatsby et React à AstroJS et Preact