Docs/Tutorial
// tutorial
Aggiungere un blog post
Il blog di ZeroToShip è file-based: nessun CMS, nessun database. Tutto il contenuto sta in app/blog/_assets/content.tsx — un singolo file TypeScript con tre array esportati: categories, authors e articles.
1. Struttura di un articolo
Un articolo è un oggetto articleType. I campi obbligatori:
{
slug: "come-lanciare-in-5-giorni",
title: "Come lanciare un SaaS in 5 giorni",
description: "La mia routine per andare da idea a MRR in una settimana.",
categories: [
categories.find((c) => c.slug === categorySlugs.tutorial),
],
author: authors.find((a) => a.slug === authorSlugs.alberto),
publishedAt: "2026-01-15",
image: {
src: cover, // import statico dell'immagine
urlRelative: "/blog/come-lanciare/cover.jpg",
alt: "Cover del post",
},
content: (
<>
<section>
<h2 className={styles.h2}>Introduzione</h2>
<p className={styles.p}>Il contenuto del post...</p>
</section>
</>
),
},Aggiungi l'oggetto in fondo all'array articles e la pagina /blog/come-lanciare-in-5-giorni è online al prossimo build.
2. Il routing
Tre pagine dinamiche leggono da questi array:
/blog/[articleId]— la pagina del singolo post (fa match conarticle.slug)/blog/category/[categoryId]— lista dei post di una categoria (match concategory.slug)/blog/author/[authorId]— tutti i post di un autore (match conauthor.slug)
Non devi toccare queste rotte, sono generiche. Se cambi uno slug, l'URL cambia automaticamente.
3. Aggiungi una categoria
Aggiungi lo slug nell'oggetto categorySlugs, poi spingi il nuovo oggetto in categories:
const categorySlugs = {
feature: "feature",
tutorial: "tutorial",
case_study: "case-study", // nuovo
};
export const categories = [
/* ... */
{
slug: categorySlugs.case_study,
title: "Case Study",
titleShort: "Case",
description: "Storie di gente che ha shippato con ZeroToShip.",
descriptionShort: "Storie di gente che ha shippato.",
},
];4. Aggiungi un autore
Stesso pattern: uno slug, un oggetto in authors, un .png o .jpg in app/blog/_assets/images/authors/:
import marioImg from "@/app/blog/_assets/images/authors/mario.png";
const authorSlugs = {
alberto: "alberto",
mario: "mario", // nuovo
};
export const authors = [
/* ... */
{
slug: authorSlugs.mario,
name: "Mario Rossi",
job: "Indie hacker",
description: "Sviluppatore freelance, scrive di SaaS.",
avatar: marioImg,
socials: [ /* opzionale */ ],
},
];5. SEO automatico
Ogni pagina blog genera automaticamente title, description, Open Graph e JSON-LD partendo dai campi dell'articolo — usa getSEOTags() da libs/seo.tsx. Il canonical URL è https://tuodominio.com/blog/[slug], e next-sitemap.config.js aggiunge automaticamente ogni post al sitemap alla build. Non devi fare nulla: scrivi il post, builda, sei indicizzabile.