Come abbiamo costruito il blog di byte5
Volevamo un blog che desse la stessa sensazione del resto di byte5.ai: veloce, statico, senza database, senza lock-in del fornitore. Nessun CMS headless, nessuna API esterna — i contenuti vivono come file nel repository e passano per lo stesso processo di review del codice.
Questo articolo ripercorre l'architettura che c'è dietro e perché scala bene per un team multilingue.
I contenuti come file#
Ogni articolo è un file MDX per lingua più una voce tipizzata nel registry. Il registry contiene tutti i campi indipendenti dalla lingua — data, tag, autore, copertina:
export const posts: BlogPostMeta[] = [
{
slug: "wie-wir-den-byte5-blog-gebaut-haben",
publishedAt: "2026-05-20",
tags: ["MDX", "Next.js", "Engineering", "byte5"],
authorId: "christian-wendler",
sourceLanguage: "de",
},
];Il bello: filtrare e ordinare nella panoramica funzionano puramente su questi dati tipizzati. Il testo vero e proprio resta dove deve stare — nel file MDX.
I contenuti appartengono al repository, non a un database. Così ogni articolo riceve la stessa cura di qualsiasi modifica al codice: branch, review, diff.
Dal markdown a un server component#
I file MDX vengono compilati in fase di build/render e resi come React Server Component. L'evidenziazione della sintassi è gestita da Shiki — interamente sul server, senza inviare un solo kilobyte di JavaScript al browser:
Perché nessuna libreria client?#
Evidenziazione, ancore dei titoli e markdown in stile GitHub avvengono tutti in fase di render. Il browser riceve HTML già pronto. Questo mantiene le pagine leggere e i Core Web Vitals verdi.
Preparare, non correre#
Un dettaglio a cui tenevamo: gli articoli con una data di pubblicazione futura non compaiono nella panoramica — eppure la pagina di dettaglio è già raggiungibile tramite link. Così gli articoli possono essere preparati con calma e vanno online automaticamente al raggiungimento della loro data.
export const revalidate = 600;
export default async function BlogIndex() {
const now = Date.now(); // valutare sempre in fase di render
const published = getPublishedPosts(now);
// ...
}Il supporto multilingue completa il quadro: un articolo viene scritto in tedesco o inglese e tradotto nelle altre lingue. Se manca una traduzione, la pagina ripiega in modo pulito sulla lingua di origine e mostra un discreto badge della lingua.
Il risultato è un blog tecnicamente noioso — nel senso migliore. È esattamente ciò che volevamo.