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

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

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.

Imagina que sólo quieres escribir tu blog, quieres construir contenido estático y después de buscar diversas opiniones en internet, te recomiendan aprender React, Vue, Angular o algún nuevo stack con el que aún no estás familiarizado. O peor aún, Wordpress.

A veces uno no necesita complicarse demasiado la vida si lo único que quiere es crear sitios HTML.

Ya que soy un desarrollador, por supuesto que en algún punto voy a necesitar algunos componentes generados dinámicamente. Por ejemplo, obtener y mostrar los dos últimos posts de mi blog.

Mi blog previo estaba escrito en GatsbyJS, que a su vez fue una migración desde Wordpress.

Durante cierto tiempo, crear páginas estáticas con Gatsby estuvo bien, pero conforme quería hacer acciones más avanzadas y mantener el mismo tipado, se volvía más complejo. Además, su API para retornar posts tenía muchos objetos dentro de objetos. Algo a lo que llamaré el “nodes hell”, el infierno de acumulación de nodos de información.

La verdadera razón detrás de todo

Si tuviera que escoger una razón fuerte por la cual elegí Astro, en lugar de otro framework como NextJS, que es muy popular en estos días, sería porque solo quiero escribir HTML simple para mis componentes o páginas, y luego tener la posibilidad de escribir artículos usando Markdown, un lenguaje muy utilizado entre los desarrolladores. No tienes que preocuparte por hacer clic entre distintas botones o usar atajos para dar formato al texto, al estilo de Word.

Todo se resume en simplicidad y poder al mismo tiempo. Cuantas más características faciliten las librerías, más tiempo podré dedicar a escribir artículos en lugar de arreglar partes de mi blog que no funcionan.

Solo ten en cuenta que Astro utiliza su propio formato de archivo: .astro. Aunque sea un nuevo formato, no tardarás en acostumbrarte, ya que en su mayoría es HTML con algunas modificaciones. Incluso puedes colocar JavaScript o TypeScript dentro del mismo archivo y será procesado por el bundler de todos modos.

Otros puntos adicionales

No usar GraphQL para un simple blog

Gatsby tiene una fuerte dependencia en GraphQL, probablemente porque era muy popular cuando Gatsby salió al mercado allá por 2017. Sus queries se utilizaban para obtener y trabajar con estructuras que venían dentro del framework.

Por ejemplo, aquí tienes una comparación para obtener los posts por idioma en 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,
},
});
}
}

Podemos ver la dependencia en GraphQL. Para probar las consultas, tenías que ir a su herramienta GraphiQL, y ahí armar tu consulta primero. En Astro, sólo dependemos de JavaScript o 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;
};

Y como Astro genera los tipos al levantar el servidor, tenemos autocompletado por defecto. Gatsby también los generaba, pero al costo de que tomaba mucho más tiempo levantar el servidor de desarrollo.

Soporte para TypeScript

Gatsby tiene buen soporte para TypeScript, pero tan pronto te salías de su manera de hacer las cosas, podía volverse complicado que tus nodos tengan tipos. Astro, incluso dentro de su propio tipo de archivo, permite utilizar TypeScript.

¿Qué tan estricta quieres que sea la configuración? Depende totalmente de ti. Astro te da la opción de escoger qué tan fuerte quieres que sea tu tipado.

Aquí tienes un ejemplo de cómo, para un botón como componente, especifico sus tipos dentro de un archivo .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>

Para obtener más información sobre la configuración de TypeScript en Astro, visita este enlace .

Desarrollo y despliegue rápidos

Recuerdo cada vez que quería comenzar a trabajar en mi entorno local de Gatsby. Tomaba más de un minuto desde que usaba su comando y, para menos de 40 páginas, era totalmente desesperante. ¿Y si quería instalar un nuevo paquete de NPM y reiniciar el servidor? ¿Y si autoguardaba el código sin completar y se detenía mi servidor local? Ahí íbamos, otro minuto más intentando levantarlo para poder continuar escribiendo o programando.

Además, Gatsby tiene tiempos de construcción de páginas muy largos. Mis últimos despliegues en Vercel mostraron tiempos promedio de 2 minutos. Para colmo, Gatsby ofrecía despliegues incrementales (incremental deployments), un servicio adicional en su propio servicio de alojamiento, que permitía sólo hacer build de los archivos modificados, en lugar de mejorar sus tiempos de construcción. Una decepción.

Con Astro, generar alrededor de 40 páginas al levantar el servidor es casi instantáneo, menos de 10 segundos. Para los despliegues, se tarda la mitad del tiempo, unos 50 segundos en promedio en noviembre de 2023.

Compatibilidad con diversos frameworks

Si aún tienes algunos plugins que solo funcionan en frameworks específicos como React, Astro te da la posibilidad de seguir usándolos. Así puedes mantener cierta funcionalidad que no quieres programar desde cero en Astro.

En general, recomendaría no usar ningún tipo de framework frontend a menos que sea necesario, ya que agrega cierto peso al archivo JavaScript final, lo que puede aumentar los tiempos de carga de tu página. Y el objetivo con Astro es mantener una ejecución totalmente ligera.

Excelente documentación

Astro, al igual que Laravel, mi framework favorito, tiene una documentación clara que muestra los tutoriales más comunes al crear un sitio web estático. Desde agregar sintaxis de código, soporte para múltiples idiomas, hasta cómo habilitar MDX en tus publicaciones, etc.

Modificar fácilmente los tags para SEO

Dado que dentro de cada componente Astro se utiliza la sintaxis de HTML, puedes usar todo tipo de etiquetas HTML, incluso <html>. Si deseas colocar algo dentro de tu <header>, como las etiquetas utilizadas para SEO, el título de la página y similares, es muy fácil escribirlo sin necesidad de utilizar componentes adicionales. Claro que, para mayor comodidad, a veces es mejor extraer tu <header> como componente.

Soporte de hosting

Astro, al generar HTML, es compatible con cientos de servicios de hosting en el mercado, incluso algunos gratuitos. Yo elegí Vercel, ya que ofrece despliegue por rama y por commit.

Mi experiencia migrando de framework

Cabe aclarar que yo no empecé migrando mi sitio desde Gatsby a Astro desde el día uno. Lo que yo quería era ver qué tantas similitudes o qué tan alta era la dificultad para implementar algunas de las características que ya tenía mi blog en Gatsby.

La verdad es que migrar hacia Astro no fue tan difícil como pensé en un principio. Astro viene con tantas herramientas y me basé fuertemente en sus tutoriales para crear un blog.

Mi experiencia usando Frontmatter también ayudó muchísimo, pero no tuve que hacer cambios drásticos en la configuración de este.

Estos son los pasos numerados que usé para migrar hacia Astro.

Pasos para comprobar la migración de mi blog Gatsby hacia Astro

  1. Instalar el proyecto basado en la plantilla de blog. Viene con algunas cosas resueltas, por ejemplo, cómo generar múltiples páginas en base al Slug.
  2. Agregar Tailwind, el cual también tiene un soporte oficial de Astro solamente siguiendo algunos pasos.
  3. Hacer un setup de Frontmatter y habilitar MDX.
  4. Crear una estructura de archivos y páginas para tener soporte multidioma, con prefijos de URL comenzando con el idioma específico. Ej: /blog y /es/blog.
  5. Traer la lista de publicaciones según el idioma. Para esto, cada uno de mis archivos tiene el mismo nombre, pero cada uno está dentro de una carpeta con las siglas del lenguaje.
  6. Comprobar que se podía tener soporte para un tema oscuro y claro. Lo logré combinando el localStorage de JavaScript con un componente Astro.
  7. Verificar que en mi página principal podía obtener los dos últimos posts basados en el día de publicación.
  8. Asignar una lista de etiquetas a cada post, luego obtener todas las etiquetas y generar una nueva página para cada etiqueta. Esta nueva página lista todos los posts en un idioma específico que contengan la etiqueta mencionada.
  9. Me aseguré de que los slug en las URL de las publicaciones se mantuvieran igual, para evitar la pérdida de indexado de Google.
  10. Crear una barra de búsqueda en el frontend que me permita ubicar mis posts según el título o la descripción.
  11. Al mismo tiempo que desarrollaba cada uno de los pasos anteriores, estaba migrando la UI completa, incluyendo clases y estilos.
  12. Hacer una migración desde React y optimización con Preact.
  13. Mejora del SEO, mejorando títulos, agregando etiquetas faltantes y corrigiendo títulos y descripciones.
  14. Despliegue en Vercel y primeras pruebas. Una vez tuve una vista previa con el 80% de las características que tenía en mi blog anterior, pasé a producción.

Algunas dificultades aparecen

A continuación, algunas de las funcionalidades que más me costaron descubrir para migrar hacia Astro.

Migrando FontAwesome

Los íconos de FontAwesome fueron una de las características más complicadas de mover, ya que hay ciertos bugs de compatibilidad entre Astro y los SVG de los íconos.

Cuando usas Gatsby u otro framework basado en React, necesitarás importar la librería @fortawesome/react-fontawesome. Para Astro, no necesitas esto.

Terminal window
# Instalar librería principal
npm i --save @fortawesome/fontawesome-svg-core
# Instalar íconos gratuitos
npm i --save @fortawesome/free-solid-svg-icons
npm i --save @fortawesome/free-regular-svg-icons
npm i --save @fortawesome/free-brands-svg-icons

Dentro de tu componente Astro, importa los iconos que necesites. Voy a colocar un código similar a como lo hice con el footer de esta página.

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

Después, usa la función icon del core para poder generar el 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];
---

Una vez almacenados los SVG de esos íconos en una nueva variable, combínalos con el componente Fragment de Astro. Este componente no necesita ser importado como el del mismo nombre que usa React. Y pásalo a su propiedad set:html.

---
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>

El ícono se renderizará ahora sin ningún problema. Si deseas darle estilo, encierra el Fragment dentro de otro componente HTML y procede a colocar las clases o los estilos allí.

Componentes Astro vs Componentes React

Los componentes Astro son muy parecidos a los componentes React. Para la migración, tuve que aprender incluso cómo pasarles propiedades y mantener el tipo. Aquí tienes un ejemplo de mi componente Astro, que es un botón donde el ícono cambia dependiendo de una variable booleana que le enviamos.

---
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>

Observa cómo indico al framework que necesito tres propiedades: const { link, title, isAppleApp } = Astro.props;

Astro no necesita ser importado, está disponible globalmente.

Después, para tener un tipado fuerte, tenemos el tipo Props. Le asignamos los tipos de las variables que especificamos en los Astro.props requeridos.

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

React plugins y uso de Preact

Debido a un par de componentes que aún conservo en mi sitio, necesité seguir usando React. Sin embargo, hay una versión más pequeña que tiene una alta probabilidad de ejecutar tus librerías React. Nos referimos a Preact, un framework minimalista que se basa en la sintaxis de React y que presume de tener solo un peso de 3KB.

Para obtener más información sobre Preact, visita este sitio .

Preact se integra con Astro en unos pocos pasos, como puedes ver en este enlace , además viene con el soporte compat. Esto es lo que activé para seguir usando mi librería de Typing animation que está en mi página principal.

Hay que tener en cuenta el tipado de los eventos en Preact, ya que no son los mismos que podrías estar acostumbrado a usar en React. Aquí tienes una demostración de mi barra de búsqueda, que es uno de los pocos componentes implementados en 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>
);
};

Mira las líneas iluminadas, sus tipados son distintos a cómo serían en React. Además, vemos que en lugar de usar onChange, usamos onInput, ya que esa es una implementación recomendada por el propio Preact.

Soporte para tema claro y oscuro usando Tailwind y Astro

En mi blog anterior en Gatsby, tenía este soporte para cambiar entre el modo día y noche en mi blog. El inconveniente era que toda esta funcionalidad usaba mucho los contextos de React. Entonces, al migrar varios de mis componentes de React hacia Astro, empecé a darle vueltas a otra solución alternativa. Además, había leído hace un par de meses que Tailwind facilita mucho el uso de clases para ambos modos.

A continuación, se muestra cómo quedó el componente Astro, que usa el localStorage del navegador para almacenar la preferencia del usuario.

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";
// Ocultar o mostrar el sol o la lune dependiendo del tema actual
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 = () => {
// Cargando íconos desde 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;
};
// Agregar el evento de cambiar de tema cuando el usuario hace click sobre la sección con 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();
});
// Cuando el DOM se ha cargado, cargar tema e íconos
window.addEventListener("load", () => {
loadTheme();
loadIcons();
});
</script>

Conclusiones

Al momento de escribir este post, mi blog lleva más de dos meses en Astro. Creo que nunca me había sentido tan cómodo y productivo trabajando con una herramienta de creación de páginas web con una documentación clara y que se ejecuta rápidamente en mi servidor de desarrollo. Además, me gusta que sea tan sencillo agregar mejoras de SEO y que todo esté documentado en su sitio oficial, sin necesidad de verificar otras fuentes de información o de tener que ir a Github para navegar entre los distintos comentarios y encontrar la solución a los problemas más comunes que podría tener todo desarrollador al trabajar con estos frameworks.

Mis posts no son generados por la IA, sin embargo, podrían estar corregidos por ella. El primer borrador siempre es de mi creación

Tags

Autor

Escrito por Helmer Davila

En otros lenguajes

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.

Why and how I moved my blog away from Gatsby and React to Astro Js and 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