wandres.dev
LAYOUTS · plantillas compartidas

El patrón head y SEO

Centralizar el head en el layout sin condenar todas las páginas al mismo título: pasar title y description como props, armar las etiquetas con datos, y extenderlo a la URL canónica y los metadatos sociales con valores por defecto.

⏱ 15 min

Centralizar el <head> en el layout resuelve la duplicación, pero abre un problema nuevo: si el <title> está fijo en el layout, todas las páginas comparten título, y para un buscador eso es casi tan malo como no tener ninguno. El patrón que lo cierra es pasar los datos del <head> como props: la página informa de su título y su descripción, y el layout arma con ellos unas etiquetas que son únicas para cada ruta pero se gestionan en un solo sitio.

🎯 Al terminar esta lección sabrás
  • Ver por qué un <head> centralizado no puede tener el título escrito a mano.
  • Pasar title y description de la página al layout mediante Astro.props.
  • Construir las etiquetas del <head> a partir de esos datos con expresiones.
  • Extenderlo a la URL canónica y a los metadatos sociales con valores por defecto seguros.

El problema de un head fijo

En la lección anterior el layout llevaba un <title>Mi sitio</title> escrito directamente. Como marco compartido funciona, pero como estrategia de SEO es un desastre: la página de contacto, la de inicio y cada artículo del blog saldrían al mundo con el mismo título en la pestaña del navegador y en los resultados de búsqueda. El buscador usa el <title> y la <meta name="description"> como la carta de presentación de cada URL; si todas dicen lo mismo, ninguna se distingue.

El objetivo, entonces, es conciliar dos fuerzas que parecen opuestas. Por un lado queremos un <head> centralizado, con toda su fontanería —codificación, viewport, enlaces a estilos, favicon— escrita una sola vez. Por otro, necesitamos que el título y la descripción cambien página a página. La solución no es renunciar a la centralización, sino parametrizarla: mantener la estructura en el layout y dejar que cada página inyecte los pocos datos que la hacen única.

Props: la página informa al layout

Un componente Astro recibe datos a través de Astro.props, y un layout, por ser un componente, no es distinto. En el frontmatter del layout desestructuras las props que esperas, y en el marcado las usas como cualquier valor.

---
// src/layouts/Base.astro
const { title, description } = Astro.props;
---
<!doctype html>
<html lang="es">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>{title}</title>
    <meta name="description" content={description} />
  </head>
  <body>
    <slot />
  </body>
</html>

Del lado de la página, las props se pasan como atributos en la etiqueta del layout, igual que pasarías atributos a cualquier componente.

---
// src/pages/contacto.astro
import Base from '../layouts/Base.astro';
---
<Base title="Contacto" description="Cómo ponerte en contacto con nosotros">
  <h1>Contacto</h1>
</Base>

Los atributos title y description de <Base> llegan al layout como Astro.props, y el layout los coloca en su <head>. El layout ha pasado de ser una estructura rígida a ser una plantilla: define la forma del documento y deja unos huecos con nombre que cada página rellena con sus datos.

Un head que se arma con datos

Con las props en la mano, el <head> deja de ser texto fijo y pasa a ser una pequeña construcción a partir de datos. Puedes aplicar un patrón de título coherente, calcular la URL canónica y ofrecer una descripción por defecto para las páginas que se olviden de darla.

---
// src/layouts/Base.astro
const { title, description = 'Sitio construido con Astro 7' } = Astro.props;
const tituloCompleto = `${title} · Mi sitio`;
const canonica = new URL(Astro.url.pathname, Astro.site);
---
<head>
  <meta charset="utf-8" />
  <title>{tituloCompleto}</title>
  <meta name="description" content={description} />
  <link rel="canonical" href={canonica} />
</head>

Aquí hay tres decisiones de diseño condensadas. El valor por defecto en description garantiza que ninguna página salga sin una descripción, aunque su autor la olvide. El tituloCompleto aplica una convención —nombre de la página, un separador, nombre del sitio— en un solo punto, así que cambiar ese formato afecta a todo el sitio a la vez. Y la URL canónica se calcula combinando Astro.site, la URL de producción declarada en astro.config.mjs, con Astro.url.pathname, la ruta de la página actual, de modo que cada página anuncia su dirección canónica sin que tú la escribas.

flowchart TD
PAG[pagina pasa title y description] --> LAY[Layout recibe las props]
LAY --> HEAD[head centralizado]
HEAD --> T[title con formato del sitio]
HEAD --> D[meta description]
HEAD --> C[link canonical]
HEAD --> OG[etiquetas open graph]
style LAY fill:#89b4fa,color:#11111b
style HEAD fill:#a6e3a1,color:#11111b

Valores por defecto y metadatos sociales

El <head> moderno no termina en el título y la descripción. Las tarjetas que se ven al compartir un enlace en redes o mensajería salen de las etiquetas Open Graph, y conviene generarlas desde los mismos datos para no mantener dos fuentes de verdad.

<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:type" content="website" />
<meta property="og:url" content={canonica} />

Reutilizar title, description y canonica para las etiquetas sociales tiene una consecuencia valiosa: la página aporta un solo conjunto de datos y el layout los proyecta en todos los formatos que los buscadores y las redes esperan. Añadir mañana una etiqueta nueva —una imagen de previsualización, una tarjeta de un tipo distinto— es editar un fichero, no cientos.

💡
Tipar las props del layout

Como el layout es un componente, puedes declarar una interfaz Props en su frontmatter y ganar autocompletado y errores tempranos. Si una página olvida title, TypeScript te avisa en el editor antes de que el sitio salga sin título. Es la diferencia entre un contrato que la máquina verifica y uno que solo vive en tu memoria.

---
interface Props {
  title: string;
  description?: string;
}
const { title, description } = Astro.props;
---
Centralizar el head convierte el SEO en una propiedad del sistema

El verdadero salto de este patrón no es escribir menos etiquetas, sino cambiar la naturaleza del problema del SEO. Cuando cada página se ocupa de su propio <head>, la calidad de tus metadatos es la suma de cientos de decisiones individuales, y basta con que una página se despiste para que salga al mundo sin descripción, sin canónica o con un Open Graph roto. El SEO se vuelve entonces una cuestión de disciplina: depende de que nadie falle nunca, que es la clase de garantía que ningún equipo puede sostener. Al mover el <head> a un layout parametrizado, inviertes esa lógica: la corrección deja de ser responsabilidad de cada página y pasa a ser una propiedad estructural del sistema. Las páginas solo aportan dos o tres datos; el resto —el formato del título, la canónica, las etiquetas sociales, los valores por defecto— lo garantiza el layout para todas por igual. Una página no puede salir con un <head> mal formado porque no es ella quien lo forma. Este desplazamiento, de la corrección por vigilancia a la corrección por construcción, es uno de los patrones más profundos de la ingeniería: siempre que puedas, no confíes en que cada actor haga lo correcto; diseña el sistema para que lo incorrecto sea imposible o, al menos, difícil. El <head> centralizado es un caso pequeño y concreto de esa idea grande, y una vez que lo ves en él lo reconoces en todas partes: la mejor manera de asegurar una propiedad no es pedirla, sino hacerla inevitable.

⚔️ Convierte el head en una plantilla
  1. Modifica tu layout para recibir title y description por Astro.props y colócalos en el <title> y la <meta name="description">.
  2. Da a description un valor por defecto y comprueba, con una página que no la pase, que sale una descripción sensata en lugar de un hueco vacío.
  3. Calcula la URL canónica con Astro.site y Astro.url.pathname, inspecciona el HTML generado de dos rutas y verifica que cada una anuncia la suya.
  4. Añade las etiquetas Open Graph reutilizando esos mismos datos y confirma que basta con editar el layout para que todas las páginas ganen la tarjeta social.