Frontend

Passer de Next.js à Astro.

Et pourquoi ce blog reste sur Next.js

Pourtant méfiant envers les nouvelles technos qui brillent, j'ai succombé à l'appel d'Astro.

Dur de passer à côté en surveillant l'écosystème JavaScript. Depuis son lancement, les retours élogieux se multiplient à son encontre et ses mises à jour ne cessent d'impressionner. L'outil se veut aujourd'hui être le prétendant idéal pour créer des blogs ou sites majoritairement statiques. Le nouveau Gatsby.

Ça tombe bien, j'écris sur un blog majoritairement statique. Réalisé avec Next.js, c'est le terrain de jeu parfait pour comparer les deux frameworks.

Pourquoi Astro ?

Le framework fait de l'excellent travail pour présenter ses atouts. Certains m'ont tapé dans l'oeil :

  • Approche zéro JavaScript par défaut, encourage la sobriété
  • Se rapproche davantage des standards du web
  • Pensé pour les blogs, intégration facilitée du MDX

Le tout enrobé de promesses pas vilaines concernant sa rapidité et le confort de développement qu'il propose. Sur le papier, tout est parfait pour créer un blog d'une fluidité sans faille.

Anatomie d'une migration

C'est parti ! Pour initialiser un projet Astro, rien de plus simple :

pnpm create astro@latest

Le coeur du site est un ensemble d'articles rédigés en MDX. Astro ne gère nativement que le Markdown mais une commande de plus nous apporte le MDX :

pnpm astro add mdx

Il est désormais temps de profiter de la notion de collection de contenus. Astro permet de profiter de la force de TypeScript pour les manipuler.

import { z, defineCollection } from "astro:content";

const articlesCollection = defineCollection({
    type: "content",
    schema: z.object({
        category: z.string(),
        title: z.string(),
        description: z.string(),
        subtitle: z.string(),
        isPublished: z.boolean(),
        publishedAt: z.date(),
        updatedAt: z.date(),
    }),
});

export const collections = {
    articles: articlesCollection,
};

En ajoutant ce contenu dans un fichier src/content/config.ts, il est désormais trivial de gérer les fichiers MDX situés dans le dossier src/content/articles.

La page d'accueil doit afficher la liste des articles ? Rien de plus simple :

---
import ArticleItem from "@components/article/ArticleItem.astro";
import Layout from "@layouts/Layout.astro";
import { getCollection } from "astro:content";

const articles = await getCollection("articles");
---

<Layout title="Welcome to Astro.">
    <main>
        <div class="mx-auto mt-20 p-6">
            <h1 class="text-4xl text-primary-600 font-semibold">
                Dernières publications
            </h1>
            <section class="flex flex-col gap-20">
                {articles.map((article) => <ArticleItem article={article} />)}
            </section>
        </div>
    </main>
</Layout>

Un très bon point pour Astro.

Les collections de contenu sont simples à utiliser, disponibles nativement. Pour reproduire des fonctionnalités équivalentes dans Next.js, il faut fournir davantage d'efforts.

Et si je veux un composant dynamique ?

C'était un point de questionnement au début de cette transition. En bas de chaque article se trouve un composant pour s'inscrire à la Newsletter.

Suivez au mieux mes découvertes

Pas besoin de vérifier ce site régulièrement, je me charge de vous partager les nouveaux articles dès leur publication.

Aucun spam, possibilité de se désinscrire en un clic. Juste une façon simple et rapide de lire mon contenu.

Rien de très compliqué ici. Le composant se contente d'envoyer l'email vers une API qui communique en retour le succès (ou l'échec) de l'opération.

Dans Next.js, un simple fichier pages/api/newsletter.ts permet de créer la partie API et React se charge de la réactivité du composant.

Pour l'API, Astro propose un mécanisme similaire avec les server endpoints. J'ai d'ailleurs pu reprendre la quasi intégralité du code de mon newsletter.ts.

Mais cela demande d'activer le mode SSR. Par défaut, Astro fonctionne uniquement en SSG.

Là aussi, le framework propose une mise en place rapide en ajoutant une ligne dans le fichier de config :

export default defineConfig({
    ...
    output: 'hybrid'
    ...
});

Avec hybrid, Astro part du principe que mon projet fonctionne encore majoritairement en SSG. Il faut préciser en haut d'un fichier si ce fonctionnement n'est pas souhaité :

export const prerender = false;
...

Si le projet exploitait majoritairement le SSR, il aurait fallu utiliser l'option server.

À partir de là, il faut ajouter un adapter pour faire tourner le code sur un serveur. Le projet étant hébergé chez Vercel, ça ne demande qu'une petite commande :

pnpm astro add vercel

Et voilà ! La partie API est en place.

Et la partie réactive ?

C'est ici qu'Astro met en avant un de ses arguments de vente. L'intégration à la carte de frameworks UI.

Par défaut, Astro vise à créer un site comportant un minimum de JavaScript. Mais il souhaite nous laisser la liberté d'utiliser nos frameworks préférés pour certaines parties de nos projets. En une commande, on peut ajouter React, Vue, Svelte, Solid, ou n'importe quel framework qui possède une intégration Astro.

À partir de là, il est possible de créer un composant Newsletter.tsx et de l'intégrer dans une page index.astro. Techniquement, il est même envisageable d'utiliser plusieurs frameworks UI au sein d'un même projet.

---
// Exemple officiel: Mélanger plusieurs frameworks sur la même page
import MyReactComponent from '../components/MyReactComponent.jsx';
import MySvelteComponent from '../components/MySvelteComponent.svelte';
import MyVueComponent from '../components/MyVueComponent.vue';
---
<div>
  <MySvelteComponent />
  <MyReactComponent />
  <MyVueComponent />
</div>

Mais je n'en vois pas l'intérêt. Combiner les frameworks me semble un bon moyen de rendre le code confus. En utiliser un seul, par contre, présente de sérieux avantages.

Ce n'est malgré tout pas l'option que j'ai choisie. Quitte à faire une refonte totale sous Astro, autant profiter de son faible coût JavaScript jusqu'au bout. En plus, le composant Newsletter n'est pas bien complexe.

---
// Exemple officiel pour intégrer un script dans un composant Astro
---
<h1>Coucou !</h1>

<script>
  console.log('Coucou la console !');
</script>

En rajoutant une balise <script> en bas d'un composant Astro, on peut tout à faire écrire du JavaScript "à l'ancienne". Et donc utiliser un Event Listener sur le formulaire et appeler l'API avec fetch. Quelques lignes à peine qui évitent de surcharger tout le site d'un framework UI.

Il faut reconnaître que l'opération reste moins agréable que de manipuler un fichier TSX. Rien que l'utilisation d'un useState simplifierait la manoeuvre. Mais ça reste un coût bien faible pour s'épargner le poids d'un framework entier.

Et ensuite ?

Derrière, le travail consiste à recoder certains composants .tsx pour en faire des .astro. Une manoeuvre qui s'est bien déroulée.

Concernant le SEO, un projet astro-seo existe pour apporter un équivalent à next-seo.

Pour le sitemap, c'est là aussi élémentaire et nativement intégré.

Et pour déployer tout ça, les options sont nombreuses et bien documentées.

MAIS

Malgré toutes ces étapes réalisées relativement rapidement, malgré les avantages réels du framework, j'ai pris la décision de ne pas utiliser cette refonte pour le moment et de rester sur Next.js.

Pourtant, Astro c'est bien

Pas de doute, le framework est déjà une alternative intéressante pour certains cas d'usage.

Le focus mis sur la simplicité et la gestion des collections de contenu le distingue dans ce secteur bondé d'options. Malgré cette volonté quasi minimaliste, Astro se veut de plus en plus complet.

J'ai débuté l'écriture de cet article en ayant des points de friction de notés. Certains n'existent déjà plus, corrigés par la dernière MAJ. Le projet est vivant, ce qu'il accomplit en ayant à peine 3 ans d'existence est conséquent.

En prime, voici quelques points positifs non évoqués :

  • La documentation est claire et riche
  • Intègre nativement Shiki qui est un plaisir à utiliser
  • La syntaxe proche de Vue me parle totalement

Mais Astro ne me convient pas totalement

Impossible de trouver un outil qui coche absolument toutes les cases. Dans le cas d'Astro, voici celles qui ne sont pas cochées dans mon cas.

Combiner avec un framework UI : oui mais non

Déjà évoqué brièvement plus haut, je ne suis pas convaincu par la possibilité d'intégrer un framework UI au sein d'un projet Astro.

C'est pourtant une force indéniable du projet et un argument qu'ils mettent grandement en avant. À raison, c'est un point de différenciation majeur et une bonne façon d'attirer un public réticent à l'idée de quitter sa zone de confort.

Compatibilité d'Astro avec différents frameworks UI
Compatibilité d'Astro avec différents frameworks UI

Mais ça ne correspond pas à mon approche. Quitte à utiliser un outil comme Astro, quitte à viser une sobriété, autant aller au bout de l'idée. Naviguer dans un projet qui combine différentes façons de créer des composants n'est pas instinctif pour moi. Je préfère avoir l'esprit uniquement focalisé sur du code React ou Vue, par exemple.

Copilot ne copilote plus très bien

J'apprécie l'apport de Github Copilot.

L'outil m'aide durant mon processus de développement et répond, souvent avec justesse, à mes questions.

Astro est encore jeune et ne cesse d'évoluer, Copilot ne peut pas proposer de réponse pertinente à son sujet. Il peut encore m'assister durant l'écriture en anticipant ce que je souhaite ajouter, mais il est à oublier en dehors de cet usage.

Cependant, ce n'est pas un défaut majeur et c'est un souci inhérent à tout projet récent.

Quelques points de friction

  • Le concept de View Transitions est intéressant et simple à mettre en place. Je n'ai juste pas accroché, probablement par manque de temps pour m'y habituer.
  • C'était ma deuxième tentative d'utilisation d'Astro pour un projet. Les deux fois, TypeScript n'a pas été coopératif. Pour une raison que j'ignore, alors que cela ne m'arrive pas sur d'autres projets (Next, Nuxt, React, Angular, ...), les nouveaux fichiers .astro ne sont pas pris en compte après leur création. L'autocomplétion ne se fait pas si je souhaite les importer ailleurs, jusqu'à ce que je redémarre le serveur TypeScript. Une manipulation à faire à chaque nouveau fichier.
  • Astro évolue vite. Très vite. C'est à la fois excitant et exigeant si l'on souhaite se tenir au courant des nouveautés et profiter pleinement du framework.
  • Importer des SVG comme composants n'est pas encore intégré nativement.
  • La commande preview ne fonctionne pas en cas d'utilisation de l'adaptateur Vercel

Vu le rythme d'évolution d'Astro, il est probable que les points évoqués ci-dessus soient obsolètes rapidement.

Bilan de l'aventure

Cette expérience m'a permis de découvrir un framework prometteur, déjà prêt à répondre à divers usages. Ce n'est pas un hasard s'il se fait une place en production, chez Porsche par exemple.

Mais ça m'a surtout permis de réaliser l'importance de deux apprentissages récents.

Penser à l'utilisateur en priorité

Faire une refonte, c'est enrichissant. Le travail effectué n'est pas perdu et je ne regrette pas d'y avoir consacré du temps.

Mais ce temps aurait pu être dédié à améliorer l'expérience utilisateur.

Performances actuelle du blog, sur mobile, selon PageSpeed
Performances actuelle du blog, sur mobile, selon PageSpeed

La version actuelle du blog est déjà performante et répond à mes attentes. Peut-être que la migration vers Astro aurait pu pousser le curseur encore plus loin. Mais l'impact auprès du public aurait été quasi nul.

Au lieu de chercher à améliorer ce qui est bon, j'aurai pu rajouter des fonctionnalités appréciables. Ce ne sont pas les idées qui manquent.

Il ne faut pas perdre de vue qu'un.e internaute ne regarde pas si notre code est rédigé avec Astro ou Next.js. Seul le résultat l'impacte.

Choose whats sparks joy

J'ai récemment découvert le formidable travail de Takuya Matsuyama. Sa chaîne YouTube est une source d'inspiration. Au-delà de l'aspect technique, je me sens en phase avec sa vision.

Dans un article récent, il fait un bilan de 7 années passées à développer un logiciel en indépendant. Un excellent article, dont certaines parties résonnent encore.

Entre autre, sa section When in doubt, choose what "sparks joy".

Il y explique que bien souvent, de multiples options sont valides pour répondre à un besoin. Des nuances existent mais fondamentalement, chaque chemin permet d'arriver au résultat souhaité.

Dans ce cas, comment choisir entre A et B ? Entre Astro et Next.js ?

Takuya propose d'opter pour ce qui amène de la joie. Ce qui sonne juste à nos yeux. L'option satisfaisante, l'option qui nous donne envie de continuer. L'impact de la motivation sera plus décisif qu'une micro nuance entre A et B.

C'est exactement ce qui explique ma décision de rester sur Next.js. Astro coche beaucoup de cases et, objectivement, je trouve l'outil adapté à mon besoin. Mais je ne prends pas autant de plaisir à l'utiliser, là où je me sens motivé en développant avec Next ou même Nuxt.

Pour continuer d'améliorer ce blog, je choisis ce qui m'amène du plaisir.

Suivez au mieux mes découvertes

Pas besoin de vérifier ce site régulièrement, je me charge de vous partager les nouveaux articles dès leur publication.

Aucun spam, possibilité de se désinscrire en un clic. Juste une façon simple et rapide de lire mon contenu.

Dernière mise à jour : 24/03/2024