Perché Keystatic + Zod mi piace assai per Next.js
Quando si sceglie un CMS per un progetto basato su Next.js, ci si trova spesso davanti a un bivio: accettare la complessità di un Headless CMS esterno (con API REST o GraphQL, chiamate di rete a ogni build e piani a pagamento) oppure scrivere manualmente file Markdown su disco senza alcuna interfaccia grafica per l'editing.
Keystatic risolve questo problema ponendosi come strumento di authoring: un'interfaccia grafica moderna, locale o integrata con GitHub, che scrive e legge normali file .mdx con frontmatter YAML direttamente nel tuo repository.
Ma c'è un dettaglio architetturale che rende questo stack straordinario quando abbinato a Zod: la garanzia della tipizzazione rigida e della validazione SEO a monte durante la build.
Zero Lock-In: i tuoi dati restano su Git
La caratteristica fondamentale di Keystatic è che non è una dipendenza del runtime di produzione. Non ci sono database da mantenere né API esterne da interrogare quando un lettore visita una pagina.
Se domani decidessi di rimuovere Keystatic dal progetto, i tuoi file .mdx rimarrebbero perfettamente intatti nel repository. Zero lock-in.
La validazione al build-time con Zod
Per garantire che tutti i contenuti rispettino le regole del tuo sito (come la presenza obbligatoria di meta-description per la SEO o l'inclusione di immagini accessibili con testo alternativo), puoi usare Zod come fonte di verità nel loader del contenuto.
Ecco come si integra la validazione nel flusso di caricamento dei dati:
import fs from "node:fs";
import path from "node:path";
import matter from "gray-matter";
import { z } from "zod";
// Schema Zod per validare il contenuto generato dal CMS
const articleSchema = z.object({
title: z.string().min(1).max(80),
date: z.coerce.date(),
// La SEO description è obbligatoria tra 50 e 160 caratteri:
// se manca o è troppo corta, il build di Next.js fallisce.
description: z.string().min(50).max(160),
status: z.enum(["beta", "rc", "production"]).default("beta"),
draft: z.boolean().default(false),
});
export function getArticle(filePath: string) {
const fileContent = fs.readFileSync(filePath, "utf8");
const { data, content } = matter(fileContent);
// Validazione rigida prima della generazione statica
const parsed = articleSchema.safeParse(data);
if (!parsed.success) {
throw new Error(
`Frontmatter non valido in ${filePath}:\n${parsed.error.message}`,
);
}
return {
...parsed.data,
body: content.trim(),
};
}I vantaggi di questo approccio
- Developer Experience eccellente: chi scrive beneficia di un'interfaccia grafica WYSIWYG moderna (accessibile anche in locale via
/keystatic). - Nessun errore in produzione: se un post dimentica un campo obbligatorio o supera i limiti di caratteri imposti per la SEO, Next.js interrompe la build segnalando l'errore esatto.
- Velocità assoluta: la generazione delle pagine statiche (SSG) avviene alla massima velocità consentita da Node.js e dal filesystem.