È il momento di creare le pagine che renderanno a video i singoli articoli del blog e a questo scopo sfrutteremo una potente funzionalità di Astro, il routing dinamico, grazie alla quale è possibile specificare parametri di percorso dinamici nel nome del file. Ad esempio [slug].astro.
Le pagine degli articoli del blog con Astro
Nella cartella /src/pages/blog creiamo il file [slug].astro. Questo agirà come template per renderizzare i singoli contenuti della collezione. All'interno del file, aggiungiamo il seguente codice:
---
import { getCollection, type CollectionEntry } from 'astro:content';
import PostLayout from '../../layouts/PostLayout.astro';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post: CollectionEntry) => ({
params: { slug: post.slug }, // L'URL della pagina (es. /blog/primo-post)
props: { post }, // I dati del post passati al componente
}));
}
const { post } = Astro.props;
const { Content } = await post.render();
---
<PostLayout frontmatter={post.data}>
<Content />
</PostLayout>
Ecco cosa fa questo codice:
PostLayout |
È il layout della pagina che creeremo più avanti. |
getStaticPaths() |
Genera le pagine del blog al momento della build, associando ogni post a un URL univoco basato sul campo slug. Per il rendering dinamico (SSR), sarà necessaria una configurazione diversa, come vedremo nella prossima lezione. |
await getCollection('blog') |
Recupera tutti i file dalla collezione blog. |
CollectionEntry |
È il tipo TypeScript delle collezioni di contenuti di Astro: qui il tipo è dato dallo schema della collezione blog. |
posts.map() |
Itera tra gli elementi della collezione. |
params |
Definisce lo slug della pagina (slug: post.slug). |
props |
È un oggetto dei dati passati alla pagina. |
post |
È un oggetto estratto dalle props attraverso una destrutturazione. |
post.render() |
Converte il contenuto Markdown del post in HTML, rendendolo disponibile per la visualizzazione tramite il componente Content. |
Salviamo i dati e andiamo a creare il PostLayout.
Il layout dei singoli post
Nella cartella /src/layouts, creiamo il file PostLayout.astro e aggiungiamo il seguente frontmatter:
---
import type { CollectionEntry } from 'astro:content';
import Layout from './Layout.astro';
interface Props {
frontmatter: CollectionEntry['data'];
}
const { frontmatter } = Astro.props;
const formattedDate = new Date(frontmatter.pubDate).toLocaleDateString('it-IT', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
---
Ecco la descrizione del codice:
import Layout from './Layout.astro';importa il layout di base contenente l'header, il footer e la struttura HTML generale del sito;interface Props { ... }definisce il tipo di dati che questo layout si aspetta di ricevere;CollectionEntry<'blog'>['data']specifica che non vogliamo l'intero oggetto post, ma solo la sua proprietàdata, contenente i dati del frontmatter del post (title,description,pubDate, ecc.).- infine, formattiamo la data per la visualizzazione.
Al di sotto del frontmatter, aggiungiamo il seguente markup:
<Layout title={frontmatter.title} description={frontmatter.description}>
<article class="max-w-4xl mx-auto">
<!-- Intestazione dell'articolo -->
<div class="mb-8 text-center">
<p class="text-gray-500">{formattedDate}</p>
<h1 class="text-4xl md:text-5xl font-extrabold mt-2">{frontmatter.title}</h1>
<p class="mt-4 text-lg text-gray-600">di {frontmatter.author}</p>
</div>
<!-- Immagine di copertina (heroImage), se presente -->
{frontmatter.heroImage && (
<img
src={frontmatter.heroImage}
alt={`Immagine di copertina per l'articolo: ${frontmatter.title}`}
class="w-full h-auto max-h-96 object-cover rounded-lg shadow-lg mb-8"
/>
)}
<!-- Qui viene inserito il contenuto del post -->
<slot />
</article>
</Layout>
<Layout ...>è il layout di base;frontmatter.titleefrontmatter.descriptionsono i dati ricevuti, che vengono passati "verso l'alto" al layout genitore;{frontmatter.heroImage && (...) }esegue il rendering condizionale dell'immagine di copertina;<slot />è il segnaposto del layout: Astro prende il contenuto compreso tra i tag<PostLayout>e</PostLayout>della pagina[slug].astroe lo inietta qui.
La prima versione del blog
Verifichiamo quanto abbiamo fatto finora. Nel terminale dei comandi, sempre posizionati nella directory del progetto, digitiamo npm run dev.
Apriamo il file /src/pages/blog/index.astro e impostiamo il numero di articoli che vogliamo visualizzare nella pagina del blog:
const POSTS_PER_PAGE = 3;
Ora possiamo testare il funzionamento del blog. Apriamo il browser all'indirizzo presente nella risposta del terminale (di solito http://localhost:4321/) e andiamo alla pagina del blog per navigare tra le anteprime dei post.
Se abbiamo creato un numero sufficiente di articoli (in formato .md o .mdx) nella cartella /src/content/blog, vedremo in fondo alla lista un pulsante "Carica altri articoli". Ogni volta che facciamo clic sul pulsante, saranno caricati altri 3 articoli, fino all'esaurimento degli articoli disponibili.

Anteprima del blog Astro
Ora facciamo clic su uno qualsiasi dei post presenti nella pagina. Se abbiamo operato correttamente, saremo indirizzati su http://localhost:4321/blog/slug-articolo, dove slug-articolo è il nome del file corrispondente al post cliccato.

Un singolo post generato da Astro
Si noti che, se uno slug non corrisponde a nessun post, Astro restituirà automaticamente un errore 404. Per personalizzare la pagina di errore, bisognerà creare un file /src/pages/404.astro, come vedremo in una lezione successiva.
Conclusioni
Il blog che abbiamo creato fino a questo punto è statico, ossia generato al momento della build. Possiamo eseguire la build e distribuire il nostro sito tramite un servizio di hosting di siti statici (per maggiori informazioni sulla distribuzione e sull'hosting, si legga la documentazione di Astro).
Inoltre, Alpine.js ci ha permesso di creare un sistema di paginazione "lato client" semplice ed efficiente: tutti i post vengono caricati inizialmente e mostrati progressivamente. Questa soluzione è perfetta per siti prevalentemente statici e blog di piccole dimensioni, in quanto Alpine carica tutti gli elementi della collezione all'inizio.
Tuttavia, le anteprime dei post non richiesti vengono comunque renderizzate, sebbene non siano visibili all'utente finché non clicca sul pulsante per caricare altri post. La conseguenza è che, all'aumentare del numero dei post, possono riscontrarsi rallentamenti. Per garantirsi la piena scalabilità del sito, è quindi opportuno pensare ad una soluzione più performante.
A questo scopo, nella prossima lezione vedremo come creare un sistema di navigazione basato sul rendering dinamico (SSR) di Astro e su htmx.
Se vuoi aggiornamenti su Astro: creare le pagine dei singoli articoli inserisci la tua email nel box qui sotto: