Entendiendo el frontmatter en Astro: sintaxis, usos y ejemplos prácticos

DUGLAS MORENODUGLAS MORENO 👁 14

Introducción

Si estás empezando con Astro, seguramente te encontraste con esas líneas de código rodeadas de triple guion (---) al inicio de un archivo .astro o .md. Ese bloque se llama frontmatter y es una de las características más poderosas del framework porque te permite pasar datos de configuración, contenido o lógica a tus componentes sin necesidad de crear archivos separados.

En este artículo vas a aprender:

  • Qué es exactamente el frontmatter y por qué Astro lo usa.
  • Cuál es su sintaxis y qué tipos de datos podés incluir.
  • Cómo definir valores simples, objetos, arreglos e incluso funciones que se ejecutan al construir el sitio.
  • Qué salida esperada tenés al usar esos datos en un componente.
  • Buenas prácticas y errores comunes para evitar sorpresas en producción.

El contenido está pensado para desarrolladores que ya saben crear un proyecto Astro básico y quieren sacarle el máximo provecho al manejo de datos. Vamos con ejemplos concretos, algunos de ellos ejecutables directamente en el navegador o Node para que veas cómo se procesa la información antes de que Astro la transforme en HTML.


¿Qué es el frontmatter?

En el mundo de los generadores de sitios estáticos (SSG) el frontmatter es una sección de metadatos que se coloca al principio de un archivo. Astro hereda esta idea de herramientas como Jekyll, Hugo o Eleventy, pero la adapta a su modelo basado en componentes.

Cuando Astro procesa un archivo:

  1. Lee todo lo que está entre los primeros --- y el segundo ---.
  2. Interpreta esa porción como código JavaScript/TypeScript (también acepta JSON puro).
  3. El resultado se convierte en un objeto que queda disponible dentro del componente mediante la propiedad Astro.props (o directamente mediante desestructuración si usas la sintaxis de props).

Esto significa que podés definir cualquier cosa que JavaScript permita: strings, números, booleanos, objetos, arreglos, funciones, importaciones, etc. El único límite es que el código debe ser sincrónico y no pueda realizar operaciones asíncronas (como fetch) dentro del frontmatter.


Sintaxis básica

El frontmatter siempre va al inicio del archivo y está delimitado por exactamente tres guiones en una línea sola, tanto de apertura como de cierre:

---
// acá va tu código JavaScript/TypeScript
---

Todo lo que esté fuera de esos bloques se considera el template del componente (HTML, expresiones {} y demás).

Ejemplo 1: frontmatter vacío

---
---
<h1>Hola mundo</h1>

Al compilarlo, el componente simplemente renderiza el título. No hay datos extra.


Tipos de datos permitidos

Astro acepta cualquier expresión que produzca un valor. Veamos los más usados.

Primitive values

---
const titulo = "Mi primer post";
const publicado = true;
const visitas = 1245;
---
<h1>{titulo}</h1>
<p>{publicado ? "Publicado" : "Borrador"}</p>
<p>Visitas: {visitas}</p>

Salida esperada

<h1>Mi primer post</h1>
<p>Publicado</p>
<p>Visitas: 1245</p>

Objetos y arreglos

---
const autor = {
  nombre: "Ana",
  twitter: "@anita_dev",
};
const etiquetas = ["astro", "frontend", "jamstack\);
---
<h2>{autor.nombre}</h2>
<p>Síguelo en {autor.twitter}</p>
<ul>
  {etiquetas.map(tag => `<li>${tag}</li>`).join("
\))}
</ul>

Salida esperada

<h2>Ana</h2>
<p>Síguelo en @anita_dev</p>
<ul>
<li>astro</li>
<li>frontend</li>
<li>jamstack</li>
</ul>

Funciones y lógica

Podés declarar funciones que se ejecuten en tiempo de build y cuyo resultado se almacene en una variable.

---
function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

const rawTitle = "guía de frontmatter\); 
const title = capitalize(rawTitle);
---
<h1>{title}</h1>

Salida esperada

<h1>Guía de frontmatter</h1>

Importar datos externos

Una de las ventajas más usadas es importar archivos JSON, Markdown o incluso otros componentes directamente desde el frontmatter.

Importar un JSON

Supongamos que tenés un archivo src/data/config.json:

{
  "siteName": "Mi Blog Astro",
  "defaultLang": "es"
}

Entonces en tu componente:

---
import config from "../data/config.json\); 
---
<header>
  <h1>{config.siteName}</h1>
  <p>Idioma por defecto: {config.defaultLang}</p>
</header>

Salida esperada

<header>
  <h1>Mi Blog Astro</h1>
  <p>Idioma por defecto: es</p>
</header>

Importar un archivo Markdown

Astro permite importar .md como si fuera un componente, y su frontmatter se expone como props.

---
import Post from "../content/posts/primer-post.md\); 
---
<Post />

Si primer-post.md contiene:

---
title: "Mi primera entrada"
date: "2024-09-01"
---

# Bienvenido

Este es el cuerpo del post.

Entonces el componente Post recibirá { title, date } y podés usarlos dentro de su propio template.


Accediendo al frontmatter desde el componente

Hay dos formas principales de leer los datos definidos en el frontmatter:

  1. Desestructuración directa (recomendado cuando sabes exactamente qué vas a usar):

    ---
    const titulo = "Título dinámico\); 
    ---
    <h1>{titulo}</h1>
  2. A través de Astro.props (útil cuando querés pasar todo el objeto a un hijo):

    ---
    const datos = { titulo: "Título", autor: "Luis" \); 
    ---
    <h1>{datos.titulo}</h1>
    <p>Por {datos.autor}</p>

    O bien, pasarlo así:

    ---
    const datos = { titulo: "Título", autor: "Luis" \); 
    ---
    <hijo { ...datos } />

    En el componente hijo:

    ---
    export interface Props {
      titulo: string;
      autor: string;
    }
    ---
    <h2>{Props.titulo}</h2>
    <p>Autor: {Props.autor}</p>

Renderizado condicional y bucles

El frontmatter no solo sirve para valores estáticos; podés usarlo para controlar qué se muestra en la página.

Mostrar un banner solo si está activo

---
const mostrarBanner = true;
const mensajeBanner = "Oferta limitada!\); 
---
{mostrarBanner && <div class="banner\)>{mensajeBanner}</div>}
<main>
  <h1>Contenido principal</h1>
</main>

Salida esperada (cuando mostrarBanner es true):

<div class="banner">Oferta limitada!</div>
<main>
  <h1>Contenido principal</h1>
</main>

Si cambias mostrarBanner a false, el <div class="banner"> desaparece completamente del HTML.

Listado dinámico de enlaces

---
const enlaces = [
  { url: \)/ \), label: "Inicio" \),
  { url: \)/blog\), label: "Blog" \),
  { url: \)/about\), label: "Acerca de" \)
];
---
<nav>
  <ul>
    {enlaces.map(link => (
      <li key={link.url}>
        <a href={link.url}>{link.label}</a>
      </li>
    ))}
  </ul>
</nav>

Salida esperada

<nav>
  <ul>
    <li><a href=\)/ \)>Inicio</a></li>
    <li><a href=\)/blog\)>Blog</a></li>
    <li><a href=\)/about\)>Acerca de</a></li>
  </ul>
</nav>

Buenas prácticas

  1. Mantélo simple y declarativo – El frontmatter es para datos, no para lógica compleja. Si necesitás hacer cálculos pesados, hacelos en un módulo separado e importá el resultado.
  2. Usá TypeScript cuando podés – Astro permite escribir el frontmatter en TypeScript (simplemente cambiando la extensión del bloque a --- y agregando lang="ts" o confiando en que el archivo tenga extensión .astro y el editor lo reconozca). Así tenés autocompletado y detección de errores temprana.
  3. Evité efectos secundarios – No pongas console.log, fetch o cualquier cosa que dependa del entorno del navegador dentro del frontmatter; esos códigos corren solo en tiempo de build y podrían romper la compilación si intentan acceder a window o document.
  4. Documentá tus campos – Si el componente va a ser reutilizado por otros devs, agregá comentarios tipo JSDoc encima de cada variable para que quede claro su tipo y propósito.
  5. Aprovechá la importación de JSON – Para configuraciones de sitio (títulos, meta tags, colores) usá archivos JSON en src/data/ y importálos donde los necesites. Así centralizás los valores y evitás duplicados.
  6. No sobrecargues el frontmatter con componentes – Aunque técnicamente podés importar un componente y usarlo como variable, es más claro mantener la separación: el frontmatter para datos, el template para presentación.

Errores comunes

  • Olvidar cerrar el bloque con --- → Astro interpreta todo el archivo como frontmatter y el template nunca se renderiza, provocando una página en blanco.
  • Usar comillas simples en JSON importado → Si el archivo JSON tiene comillas simples (') en lugar de dobles ("), fallará el parseo y el build se detendrá.
  • Confundir Astro.props con la desestructuración → Si hacés const { titulo } = Astro.props; pero en el frontmatter definiste const titulo = ...;, la variable ya está en el scope y no necesitás acceder a Astro.props. Ambos métodos son válidos, pero mezclar mal puede dar undefined.
  • Intentar usar await dentro del frontmatter → Astro no soporta código asíncrono allí; el build fallará con un mensaje como "Frontmatter must be synchronous)." Si necesitás datos asíncronos, usá el endpoint de getStaticPaths o cargalos en el cliente.
  • Declarar variables con el mismo nombre que propiedades de Astro → Por ejemplo, hacer const Astro = {}; sobrescribe el objeto que Astro provee y rompe el acceso a props y métodos.

Conclusión

El frontmatter es una de esas características que, aunque parezca sencilla, abre un montón de posibilidades para mantener tus componentes limpios, reutilizables y fáciles de configurar. Aprendiste a:

  • Definir valores primitivos, objetos y arreglos.
  • Ejecutar funciones simples para derivar datos en tiempo de build.
  • Importar JSON y Markdown para separar configuración y contenido.
  • Acceder a esos datos tanto mediante desestructuración como vía Astro.props.
  • Usar el frontmatter para renderizado condicional y listas dinámicas.
  • Aplicar buenas prácticas y evitar los errores más frecuentes.

Con esta base ya podés comenzar a crear componentes más inteligentes: por ejemplo, un <SEO /> que lea su título y descripción del frontmatter, o un <Card /> que cambie su estilo según un prop variant definido en el inicio del archivo.

Si te quedó alguna duda o querés ver un caso de uso más avanzado (como generar rutas dinámicas a partir de un archivo CSV), dejá un comentario y seguimos explorando. ¡Hasta la próxima!


Este artículo fue escrito pensando en la versión actual de Astro (v4.x), pero la mayoría de los conceptos son compatibles con versiones anteriores.


Apéndice: ejemplo ejecutable de extracción de frontmatter

Para que veás cómo Astro interpreta el bloque entre ---, acá tenés un pequeño script en JavaScript que toma un string con frontmatter y devuelve el objeto resultante. Este código no forma parte de Astro, pero ilustra el proceso de parseo que hace el compilador internamente.

function parseFrontmatter(text) {
  const match = text.match(/^---
?
([\s\S]*?)
?
---/);
  if (!match) return {};
  const code = match[1];
  try {
    // Usamos Function para ejecutar el código en un scope aislado
    const fn = new Function(`return { ${code} }`);
    return fn();
  } catch (e) {
    console.error("Error al ejecutar frontmatter:\), e);
    return {};
  }
}

const ejemplo = `
---
const titulo = "Ejemplo dinámico\); 
const numero = 42;
const lista = ["a", "b", "c\);
---
<h1>{titulo}</h1>
<p>{numero}</p>
<ul>{lista.map(i => `<li>${i}</li>`).join("
\)}</ul>
`;

const datos = parseFrontmatter(ejemplo);
console.log(datos);
// Salida esperada:
// { titulo: 'Ejemplo dinámico', numero: 42, lista: [ 'a', 'b', 'c' ] }

Al ejecutar este bloque verás en la consola el objeto que Astro habría puesto a disposición del componente. Este tipo de utilidad es útil si querés crear tus propias herramientas de inspección o generación de sitios.


¡Listo! Ahora tenés un panorama completo del frontmatter en Astro y podés aplicarlo con confianza en tus próximos proyectos.

Comentarios (0)

Sé el primero en comentar.

Dejá tu comentario