Ir al contenido

Crear un blog

Esta guía te acompaña paso a paso para crear un blog con EmDash, desde la definición del contenido hasta la publicación y el filtrado por categorías y etiquetas.

  • Un sitio EmDash configurado y en funcionamiento (ver Primeros pasos)
  • Conocimientos básicos de componentes de Astro

EmDash crea una colección posts por defecto durante la configuración. Si necesitas personalizarla, puedes hacerlo desde el panel de administración o mediante la API.

La colección de publicaciones por defecto incluye:

  • title - Título de la publicación
  • slug - Identificador amigable para la URL
  • content - Cuerpo del texto enriquecido
  • excerpt - Descripción corta
  • featured_image - Imagen de cabecera (opcional)
  • status - borrador, publicado o programado
  • publishedAt - Fecha de publicación (campo del sistema)
  1. Abre el panel de administración en /_emdash/admin

  2. Haz clic en Posts en la barra lateral

    EmDash posts list showing titles, status, and dates
  3. Haz clic en New Post

    EmDash post editor with title, content, and publish options
  4. Introduce un título y escribe tu contenido usando el editor de texto enriquecido

  5. Añade categorías y etiquetas en el panel lateral

  6. Establece el estado en Published

  7. Haz clic en Save

Tu publicación queda publicada al instante. No hace falta reconstruir el sitio.

Crea una página que muestre todas las publicaciones publicadas:

src/pages/blog/index.astro
---
import { getEmDashCollection } from "emdash";
import Base from "../../../layouts/Base.astro";
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
// Ordenar por fecha de publicación, la más reciente primero
const sortedPosts = posts.sort(
(a, b) => (b.data.publishedAt?.getTime() ?? 0) - (a.data.publishedAt?.getTime() ?? 0)
);
---
<Base title="Blog">
<h1>Blog</h1>
<ul>
{sortedPosts.map((post) => (
<li>
<a href={`/blog/${post.data.slug}`}>
<h2>{post.data.title}</h2>
<p>{post.data.excerpt}</p>
<time datetime={post.data.publishedAt?.toISOString()}>
{post.data.publishedAt?.toLocaleDateString()}
</time>
</a>
</li>
))}
</ul>
</Base>

Crea una ruta dinámica para publicaciones individuales:

src/pages/blog/[slug].astro
---
import { getEmDashCollection, getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";
import Base from "../../../layouts/Base.astro";
export async function getStaticPaths() {
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
return posts.map((post) => ({
params: { slug: post.data.slug },
}));
}
const { slug } = Astro.params;
const { entry: post } = await getEmDashEntry("posts", slug);
if (!post) {
return Astro.redirect("/404");
}
---
<Base title={post.data.title}>
<article>
{post.data.featured_image && (
<img src={post.data.featured_image} alt="" />
)}
<h1>{post.data.title}</h1>
<time datetime={post.data.publishedAt?.toISOString()}>
{post.data.publishedAt?.toLocaleDateString()}
</time>
<PortableText value={post.data.content} />
</article>
</Base>

EmDash incluye taxonomías de categoría y etiqueta integradas. Consulta Taxonomías para obtener detalles sobre cómo crear y gestionar términos.

src/pages/category/[slug].astro
---
import { getEmDashCollection, getTerm, getTaxonomyTerms } from "emdash";
import Base from "../../../layouts/Base.astro";
export async function getStaticPaths() {
const categories = await getTaxonomyTerms("category");
// Aplanar categorías jerárquicas
const flatten = (terms) => terms.flatMap((t) => [t, ...flatten(t.children)]);
return flatten(categories).map((cat) => ({
params: { slug: cat.slug },
props: { category: cat },
}));
}
const { category } = Astro.props;
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
where: { category: category.slug },
});
---
<Base title={category.label}>
<h1>{category.label}</h1>
{category.description && <p>{category.description}</p>}
<ul>
{posts.map((post) => (
<li>
<a href={`/blog/${post.data.slug}`}>{post.data.title}</a>
</li>
))}
</ul>
</Base>

Muestra las categorías en publicaciones individuales:

src/components/PostMeta.astro
---
import { getEntryTerms } from "emdash";
interface Props {
postId: string;
}
const { postId } = Astro.props;
const categories = await getEntryTerms("posts", postId, "category");
const tags = await getEntryTerms("posts", postId, "tag");
---
<div class="post-meta">
{categories.length > 0 && (
<div class="categories">
<span>Categorías:</span>
{categories.map((cat) => (
<a href={`/category/${cat.slug}`}>{cat.label}</a>
))}
</div>
)}
{tags.length > 0 && (
<div class="tags">
<span>Etiquetas:</span>
{tags.map((tag) => (
<a href={`/tag/${tag.slug}`}>{tag.label}</a>
))}
</div>
)}
</div>

Para blogs con muchas publicaciones, añade paginación:

src/pages/blog/page/[page].astro
---
import { getEmDashCollection } from "emdash";
import Base from "../../../../layouts/Base.astro";
const POSTS_PER_PAGE = 10;
export async function getStaticPaths() {
const { entries: allPosts } = await getEmDashCollection("posts", {
status: "published",
});
const totalPages = Math.ceil(allPosts.length / POSTS_PER_PAGE);
return Array.from({ length: totalPages }, (_, i) => ({
params: { page: String(i + 1) },
props: { currentPage: i + 1, totalPages },
}));
}
const { currentPage, totalPages } = Astro.props;
const { entries: allPosts } = await getEmDashCollection("posts", {
status: "published",
});
const sortedPosts = allPosts.sort(
(a, b) => (b.data.publishedAt?.getTime() ?? 0) - (a.data.publishedAt?.getTime() ?? 0)
);
const start = (currentPage - 1) * POSTS_PER_PAGE;
const posts = sortedPosts.slice(start, start + POSTS_PER_PAGE);
---
<Base title={`Blog - Página ${currentPage}`}>
<h1>Blog</h1>
<ul>
{posts.map((post) => (
<li>
<a href={`/blog/${post.data.slug}`}>{post.data.title}</a>
</li>
))}
</ul>
<nav>
{currentPage > 1 && (
<a href={`/blog/page/${currentPage - 1}`}>Anterior</a>
)}
<span>Página {currentPage} de {totalPages}</span>
{currentPage < totalPages && (
<a href={`/blog/page/${currentPage + 1}`}>Siguiente</a>
)}
</nav>
</Base>

Crea un feed RSS para tu blog:

src/pages/rss.xml.ts
import rss from "@astrojs/rss";
import { getEmDashCollection } from "emdash";
export async function GET(context) {
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
return rss({
title: "Mi blog",
description: "Un blog creado con EmDash",
site: context.site,
items: posts.map((post) => ({
title: post.data.title,
pubDate: post.data.publishedAt,
description: post.data.excerpt,
link: `/blog/${post.data.slug}`,
})),
});
}

Instala el paquete RSS si aún no lo has hecho:

Ventana de terminal
npm install @astrojs/rss