wandres.dev
SEO · metadatos, OG, JSON-LD

Metadatos base: title, description y un componente SEO

El punto de partida del SEO en Astro 7: escribir títulos y descripciones que respeten los límites del buscador, encapsular esos metadatos en un componente reutilizable y tipado, y derivar la URL canónica autorreferente desde Astro.url y Astro.site para que ninguna página salga al mundo sin su carta de presentación.

⏱ 16 min

Cada página de tu sitio se presenta ante un buscador con dos frases: un título y una descripción. No son decoración, son el contrato que decide si alguien hace clic desde los resultados o sigue de largo. En este nivel dejamos de tratar el SEO como un puñado de etiquetas sueltas y lo tratamos como lo que es —una interfaz entre tu contenido y una máquina que lo interpreta— y empezamos por su pieza más básica: reunir título, descripción y URL canónica en un único componente <SEO /> que toda página reutilice, para que la corrección de los metadatos deje de depender de la memoria de quien escribe cada ruta.

🎯 Al terminar esta lección sabrás
  • Escribir títulos y descripciones que respeten los límites del buscador y aporten una señal única por página.
  • Encapsular los metadatos base en un componente <SEO /> reutilizable y tipado.
  • Derivar la URL canónica autorreferente con Astro.url y Astro.site.
  • Dar valores por defecto seguros para que ninguna ruta salga sin metadatos.

El título y la descripción: el contrato con el buscador

El elemento <title> es la señal en página más determinante que controlas: alimenta la pestaña del navegador, el enlace azul del resultado de búsqueda y el texto que se copia al compartir. Un buscador lo usa para entender de qué trata la página, así que ha de ser específico, único y cargar la palabra clave importante al principio. Los resultados se truncan por ancho en píxeles, no por número de caracteres, pero como regla de bolsillo un título que ronda los 55 o 60 caracteres rara vez se corta. La convención madura es un patrón título de la página · marca, que da contexto sin gastar el espacio útil en el nombre del sitio.

La <meta name="description"> no es un factor de posicionamiento —el buscador no te sube por escribirla bien— pero sí un factor de clics: es el fragmento que acompaña al título en el resultado y convence, o no, de entrar. Apunta a unos 150 caracteres, resume la promesa real de la página y evita el relleno. Conviene aceptar desde ya una verdad incómoda: el buscador reescribe a menudo ese fragmento con el trozo de tu contenido que mejor casa con la consulta, de modo que tu descripción es una propuesta, no una garantía. Escribirla bien inclina la balanza; no la decide.

Un defecto sorprendentemente común, y fácil de evitar con este enfoque, es el título duplicado: decenas de páginas que comparten el mismo <title> porque nadie se ocupó de diferenciarlas. Para el buscador, títulos idénticos son páginas indistinguibles, y unas acaban canibalizando a las otras por las mismas consultas. Derivar el título de un dato propio de cada página —su encabezado, su nombre en la colección— y componerlo en un solo sitio elimina de raíz la clase entera de ese error, porque la unicidad deja de depender de que alguien se acuerde.

---
// El patrón "página · marca" en un solo lugar
const { title } = Astro.props;
const tituloCompleto = title ? `${title} · nvim-dios` : 'nvim-dios';
---
<title>{tituloCompleto}</title>

Un componente <SEO /> como única fuente

En el nivel de layouts ya centralizaste el <head>; aquí damos el paso siguiente: extraer los metadatos a su propio componente <SEO />. La diferencia no es cosmética. Un layout mezcla estructura visual con metadatos; un componente dedicado aísla la lógica de SEO en una pieza que se prueba, se razona y se reutiliza por sí sola, incluso en páginas que no comparten layout. Es el mismo principio de responsabilidad única que aplicas al código: el objeto que sabe de buscadores no debería saber también de barras de navegación.

---
// src/components/SEO.astro
interface Props {
  title: string;
  description: string;
  canonical?: string | URL;
  noindex?: boolean;
}
const {
  title,
  description,
  canonical = new URL(Astro.url.pathname, Astro.site),
  noindex = false,
} = Astro.props;
const tituloCompleto = `${title} · nvim-dios`;
---
<title>{tituloCompleto}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonical.toString()} />
{noindex && <meta name="robots" content="noindex, nofollow" />}

La interfaz Props convierte el contrato en algo que la máquina verifica: si una página olvida title, TypeScript protesta en el editor antes de que el sitio salga sin título. El componente se coloca en el <head> del layout y cada página le pasa sus dos o tres datos, con lo que el <head> deja de ser una lista de etiquetas escritas a mano y pasa a ser el resultado de invocar una función bien tipada.

---
// src/layouts/Base.astro
import SEO from '../components/SEO.astro';
const { title, description } = Astro.props;
---
<html lang="es">
  <head>
    <meta charset="utf-8" />
    <SEO title={title} description={description} />
  </head>
  <body><slot /></body>
</html>

Resiste la tentación de que este componente haga demasiado. Su trabajo es emitir metadatos, no maquetar; si mañana suma las etiquetas Open Graph y el bloque JSON-LD de las próximas lecciones, seguirá siendo una sola pieza con una sola razón para cambiar. Y como no depende de nada visual, puedes razonarlo y probarlo aislado: dado un conjunto de props, ¿emite las etiquetas correctas? Esa pregunta tiene una respuesta verificable, que es justo lo que se pierde cuando el SEO se dispersa por veinte plantillas.

La canónica autorreferente con Astro.url

Una misma página puede alcanzarse por varias direcciones —con barra final y sin ella, con parámetros de campaña, por http y por https— y el buscador puede tratarlas como duplicados que se reparten la autoridad. La <link rel="canonical"> corta ese nudo nombrando la dirección verdadera. La práctica robusta es la canónica autorreferente: cada página declara como canónica su propia URL limpia, derivada de Astro.site más Astro.url.pathname, nunca escrita a mano.

Derivarla en vez de copiarla tiene una consecuencia valiosa: es correcta por construcción en cualquier entorno, porque se recalcula de la fuente. Un matiz fácil de olvidar es que la canónica debe casar con la forma real de tus URLs —la que fija trailingSlash—; tomarla de Astro.url en lugar de componerla a mano evita justo ese desajuste, porque usa la ruta tal como el sitio la resuelve. Y como el componente <SEO /> la calcula por defecto, ninguna página puede quedarse sin ella salvo que la sobrescriba a propósito.

Valores por defecto: que ninguna página quede desnuda

El poder real de encerrar el SEO en un componente es que puede fallar hacia la seguridad. Si una página olvida un dato, el componente no debe emitir una etiqueta vacía, sino un valor por defecto sensato: una descripción genérica del sitio, la imagen social de reserva, un index, follow implícito para las páginas normales. Así el peor caso de un descuido no es una página muda ante el buscador, sino una página con metadatos correctos aunque menos afinados.

---
// src/components/SEO.astro, con defaults defensivos
const {
  title,
  description = 'Guía de Neovim, Astro y desarrollo moderno',
  image = '/og-default.png',
  noindex = false,
} = Astro.props;
const canonical = new URL(Astro.url.pathname, Astro.site);
const ogImage = new URL(image, Astro.site);
---
<title>{title ? `${title} · nvim-dios` : 'nvim-dios'}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonical.href} />
<meta name="robots" content={noindex ? 'noindex, nofollow' : 'index, follow'} />

Fíjate en que el <title> contempla el caso de la portada, donde el patrón página · marca sobra y basta el nombre del sitio a secas. Estos condicionales viven en un único lugar, de modo que la política de metadatos del sitio entero cabe en un fichero que puedes leer de un vistazo y razonar por completo. Cambiar la descripción por defecto, el sufijo del título o la imagen de reserva es editar una línea, y el cambio se propaga a cada ruta sin que ninguna tenga voto ni pueda desviarse.

🏷️

title

La señal en página más fuerte. Único, específico, con la marca al final y bajo los 60 caracteres.

📝

description

No posiciona, pero gana clics. Un resumen honesto de 150 caracteres que el buscador puede reescribir.

🎯

canonical

La URL verdadera, autorreferente y derivada de Astro.url, para que los duplicados no dividan la autoridad.

flowchart TD
PAG[pagina pasa title y description] --> SEO[componente SEO]
URL[Astro url y Astro site] --> SEO
SEO --> T[title con patron marca]
SEO --> D[meta description]
SEO --> C[link canonical autorreferente]
T --> SERP[resultado de busqueda]
D --> SERP
C --> SERP
style SEO fill:#89b4fa,color:#11111b
style SERP fill:#a6e3a1,color:#11111b
💡
El truncado se mide en píxeles, no en letras

Un título de mayúsculas anchas se corta antes que uno de minúsculas estrechas aunque tengan las mismas letras, porque el buscador trunca por ancho renderizado. Por eso las cifras de 60 caracteres para el título y 150 para la descripción son orientaciones, no fronteras exactas. Escribe pensando en que lo esencial vaya al principio: si el final se corta, que sea prescindible.

⚠️
La canónica absoluta o no sirve

Una canónica con ruta relativa es inservible: el buscador necesita una URL absoluta con esquema y dominio para identificar la página. Resuélvela siempre contra Astro.site con new URL, y asegúrate de que Astro.site esté declarado en la configuración, porque sin él la derivación falla en silencio y la etiqueta sale vacía o rota.

El resultado de búsqueda es un lienzo que no controlas

Lo profundo de esta primera lección no es la lista de etiquetas, sino un cambio de encuadre sobre qué es el SEO. Cuando publicas una página, estás enviando señales a un sistema ajeno que las renderiza a su manera, en un contexto —el resultado de búsqueda— cuyo aspecto final tú no decides: el buscador elige qué título mostrar, reescribe la descripción según la consulta, resuelve qué URL de entre las duplicadas premia. No pintas el resultado; propones los datos con los que otro lo pinta. Interiorizar esto reordena todas tus prioridades. Deja de importar la etiqueta perfecta para una consulta imaginaria y empieza a importar la honestidad y la consistencia de las señales: un título que describe de verdad la página, una descripción que no promete lo que la página no cumple, una canónica que apunta sin ambigüedad a una sola dirección. Y como esas señales son un contrato que se repite en cientos de rutas, la única forma de sostenerlo es no confiarlo a la disciplina de nadie, sino codificarlo en un objeto —el componente <SEO />— que lo garantice por construcción. Ese componente es un objeto frontera: vive en el límite entre tu sistema, que conoce el contenido, y el sistema del buscador, que conoce a los usuarios. Diseñar bien esa frontera —tiparla, derivar de la fuente lo derivable, dar valores por defecto seguros— es diseñar la conversación entre dos máquinas que nunca se hablarán directamente. El SEO técnico, visto así, no es cazar trucos, sino cuidar la calidad de una interfaz.

⚔️ Encapsula tus metadatos en un componente
  1. Crea src/components/SEO.astro con una interfaz Props que exija title y description y acepte canonical y noindex opcionales.
  2. Aplica el patrón título · marca en un solo punto y comprueba, con dos páginas distintas, que cada <title> sale único.
  3. Deriva la canónica por defecto de Astro.site y Astro.url.pathname, inspecciona el HTML de dos rutas y verifica que cada una declara la suya, absoluta.
  4. Coloca el <SEO /> en el <head> del layout, pásale los datos desde una página, y confirma que olvidar title provoca un error de TypeScript en el editor y no una página muda en producción.