🧠 Implementación de SEO dinámica en Astro con Markdown y páginas .astro
En este artículo aprenderás a implementar un sistema escalable para SEO técnico en tu proyecto Astro. Utilizaremos contenido en Markdown y páginas .astro
, conectando todo con un componente dinámico que genera metaetiquetas automáticamente para Open Graph, Twitter Card y más.
📚 Índice
- 1. Desde un archivo .md (Markdown Content Collection)
- 2. Desde una página .astro
- 3. ¿Qué hace el Layout.astro?
- 4. ¿Qué pasa si falta un campo?
- 5. Componente Seo.astro completo
- 6. Cómo usarlo en tu Layout.astro
- 7. Resultado final
1. Desde un archivo .md
(Markdown Content Collection)
Supongamos que tienes un archivo en:
src/content/blog/ejemplo.md
Con este frontmatter:
---
title: "Título del artículo"
description: "Descripción optimizada para SEO"
pubDate: "2025-07-19"
author: "Leandro Fuentes"
image: "/blog/ejemplo.jpg"
tags: ["markdown", "astro", "seo"]
---
✔️ ¿Qué sucede?
- El frontmatter se pasa automáticamente como props al
Layout.astro
cuando usas un archivo como[slug].astro
. - El layout luego pasa estos datos al componente
<Seo />
.
2. Desde una página .astro
Supongamos que tienes esta página:
src/pages/ejemplo.astro
---
import Layout from "../layouts/Layout.astro";
---
<Layout
title="Página de ejemplo"
description="Esta es una página construida con Astro"
pubDate="2025-07-19"
author="Leandro Fuentes"
image="/blog/ejemplo.jpg"
>
<h1>Contenido de la página</h1>
</Layout>
✔️ ¿Qué sucede?
- Estás pasando los props manualmente al
Layout.astro
- El layout los redirige al componente
<Seo />
3. ¿Qué hace Layout.astro
?
---
import Seo from "../components/Seo.astro";
const { title, description, pubDate, author, image } = Astro.props;
---
<html lang="es">
<head>
<Seo
title={title}
description={description}
pubDate={pubDate}
author={author}
image={image}
url="https://leandrofuentes.com"
/>
<!-- Otros metatags, favicon, fuentes, etc. -->
</head>
<body>
<slot />
</body>
</html>
✅ Esto genera automáticamente todas las metaetiquetas SEO, incluyendo Open Graph y Twitter Card para compartir en redes sociales.
4. ¿Qué pasa si falta un campo?
El componente Seo.astro
incluye valores por defecto, así que:
- Si no pasas
author
, se usará"Leandro Fuentes"
- Si no pasas
image
, se usará"/default-og.jpg"
- Si no pasas
url
, se usará"https://leandrofuentes.com"
🛡 Esto evita errores o previews incompletos.
5. Componente Seo.astro
completo
Guarda este archivo como:
src/components/Seo.astro
---
// src/components/Seo.astro
export interface Props {
title: string
description: string
pubDate?: string
author?: string
image?: string
url?: string
}
const {
title,
description,
pubDate = "",
author = "Leandro Fuentes",
image = "/default-og.jpg",
url = "https://leandrofuentes.com"
} = Astro.props;
---
<!-- SEO Meta Tags -->
<title>{title}</title>
<meta name="description" content={description} />
<meta name="author" content={author} />
<meta name="robots" content="index, follow" />
<!-- Open Graph -->
<meta property="og:type" content="article" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={url + image} />
<meta property="og:url" content={Astro.url.origin + Astro.url.pathname} />
<meta property="og:site_name" content="Leandro Fuentes - Developer & Designer" />
{pubDate && <meta property="article:published_time" content={pubDate} />}
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={url + image} />
6. Cómo usarlo en tu Layout.astro
Dentro del <head>
de tu layout principal:
---
import Seo from '../components/Seo.astro';
const { title, description, pubDate, author, image } = Astro.props;
---
<html lang="es">
<head>
<Seo
title={title}
description={description}
pubDate={pubDate}
author={author}
image={image}
url="https://leandrofuentes.com"
/>
<!-- Otros metatags, fuentes, favicon, etc. -->
</head>
7. ✅ Resultado final
Este sistema:
- Extrae metadatos desde el frontmatter o props
- Genera etiquetas SEO válidas y completas
- Automatiza previews en WhatsApp, Facebook, LinkedIn, X (Twitter)
- Escala en todos los artículos del blog con una sola fuente de configuración
¿Quieres más plantillas o integraciones con JSON-LD, Schema.org o Favicon dinámico? ¡Pídelo y lo agregamos!
⚠️ Advertencia: por qué tu metaetiqueta puede no estar funcionando
Es posible que, aunque hayas definido correctamente el title
y description
en el frontmatter de tu .md
, al revisar el HTML generado veas algo como esto:
<meta property="og:title" content="Leandro Fuentes - Developer & Designer | Marketing Digital" />
<title>Leandro Fuentes - Developer & Designer | Marketing Digital</title>
En lugar de ver el título real de tu artículo.
🧠 ¿Por qué ocurre esto?
Esto pasa cuando en el layout (Layout.astro
) estás usando valores por defecto así:
const {
title = "Leandro Fuentes - Developer & Designer | Marketing Digital",
description = "Sitio web personal que muestra mi trabajo y experiencia",
pubDate, author, image, url
} = Astro.props;
En ese caso, aunque el .md
tenga title
y description
, el layout ya les está asignando un valor por defecto y no se actualizan.
✅ ¿Cómo solucionarlo?
Haz lo siguiente:
-
Asegúrate de que el post tenga el layout bien referenciado (esto ya lo tienes).
-
En el
Layout.astro
, NO asignes valores por defecto entitle
nidescription
. Usa:
const { title, description, pubDate, author, image, url } = Astro.props;
- Si quieres tener un fallback para casos donde no se pase nada, hazlo dentro del componente
Seo.astro
, no en el layout.
🎯 Resultado
Esto asegura que el título y descripción definidos en tu Markdown se apliquen correctamente en las etiquetas <title>
y og:title
, sin que sean sobrescritos por valores por defecto del layout.