wandres.dev
TEMPLATING · expresiones y directivas

Listas y renderizado condicional

Renderizar colecciones mapeando arrays a marcado con .map, el papel real de las keys en un framework que no reconcilia, y el renderizado condicional con el operador logico y el ternario, con la trampa de los valores falsy.

⏱ 14 min

Una plantilla que solo interpola valores sería inútil: necesita repetir y decidir. Astro no añade sintaxis nueva para ello, usa el JavaScript que ya produce valores: .map para listas, el operador && y el ternario para condiciones. Como la llave solo admite expresiones, toda la lógica de control se expresa con herramientas que devuelven algo interpolable. Y como Astro no reconcilia, esas herramientas se comportan de forma más simple de lo que esperas.

🎯 Al terminar esta lección sabrás
  • Renderizar una colección mapeando un array a marcado con .map.
  • Entender por qué las keys son opcionales en Astro y qué las diferencia de React.
  • Mostrar u ocultar marcado con el operador && y elegir entre dos ramas con el ternario.
  • Evitar la trampa de los valores falsy, en especial el 0 que sí pinta.

Listas con .map

Para renderizar una colección, mapeas el array a un array de nodos de plantilla. Astro renderiza un array concatenando sus elementos en su lugar, así que la llave contiene una sola expresión —la llamada a .map— que devuelve tantos elementos como datos haya.

---
const lenguajes = ['Rust', 'TypeScript', 'Go'];
---
<ul>
  {lenguajes.map((lang) => <li>{lang}</li>)}
</ul>

Al ser JavaScript puro, encadenas lo que necesites antes de pintar: filtras, ordenas o accedes a campos de objetos dentro del propio .map. La plantilla se lee mejor cuando la transformación pesada vive arriba: calcula la lista final en el frontmatter y deja en la plantilla solo el .map que la proyecta.

Merece la pena saber qué hace Astro con cada tipo de valor, porque .map produce justamente un array. Un array se concatena elemento a elemento; los arrays anidados se aplanan; los false, null y undefined que aparezcan dentro se omiten sin ruido. Eso te permite mezclar en una misma estructura elementos y condiciones y confiar en que las ramas falsas simplemente no pinten, sin romper la concatenación del resto.

---
const mostrarBanner = false;
const filas = ['a', 'b'];
---
<section>
  {[
    <h2>Panel</h2>,
    mostrarBanner && <p class="banner">Aviso</p>,
    ...filas.map((f) => <div>{f}</div>),
  ]}
</section>

En ese array conviven un elemento fijo, una condición con && y una lista expandida con el operador de propagación. Astro los aplana y concatena; la rama mostrarBanner && ..., al ser falsa, se evapora sin dejar hueco. Es la prueba de que renderizar listas y renderizar condiciones son, por debajo, el mismo gesto: producir valores que Astro concatena.

---
const posts = [
  { titulo: 'Islas', min: 8 },
  { titulo: 'Rutas', min: 12 },
];
---
<ul>
  {posts
    .filter((p) => p.min < 10)
    .map((p) => <li>{p.titulo} en {p.min} min</li>)}
</ul>
💡
Varios elementos por iteración piden Fragment

Si dentro del .map cada dato debe producir más de un elemento hermano —una fila con varias celdas, un <dt> y su <dd>—, no puedes devolver dos raíces sueltas: la función de mapeo necesita una sola. Ahí entra <Fragment>, que agrupa esos hermanos sin añadir un contenedor al DOM. Es el puente natural con la próxima lección, y la razón de que ambas vivan juntas en este nivel.

Las keys: un artefacto que Astro no necesita

Quien viene de React esperará una key en cada elemento mapeado, y el aviso de consola cuando falta. En Astro no hay tal aviso, y no es un olvido. La key existe en React para la reconciliación: cuando el componente se re-renderiza, React compara el árbol viejo con el nuevo y usa la key como identidad para mover lo mínimo. Astro nunca reconcilia; renderiza la lista una vez, la emite como HTML y termina. Sin re-render no hay nada que reconciliar, y sin reconciliación la key no tiene trabajo que hacer.

Puedes escribir key si quieres —Astro no protesta—, pero en la salida estática es inerte: ni siquiera llega al HTML final. La única excepción vive dentro de una isla: si el elemento mapeado es un componente de un framework hidratado en el cliente, las reglas de ese framework aplican dentro de su isla. Pero eso es asunto de React o Svelte, no de la plantilla de Astro que los rodea.

📝
La ausencia de key no es una carencia

Que Astro no pida keys no significa que le falte una función: significa que le sobra el problema. Las keys son el precio de un DOM virtual que se reconcilia en cada render. Astro no paga ese precio porque no tiene ese DOM virtual ni ese ciclo: emite HTML una sola vez. Menos maquinaria, menos ceremonia, y una regla menos que recordar al escribir listas.

Condicionales: el operador && y el ternario

Sin if dentro de la llave, los condicionales se expresan con operadores que devuelven un valor. El && sirve para mostrar u ocultar un bloque; el ternario, para elegir entre dos alternativas reales.

---
const usuario = { nombre: 'Ada', admin: true };
const avisos = [];
---
{usuario.admin && <a href="/panel">Panel de control</a>}

<p>{usuario.nombre ? `Hola, ${usuario.nombre}` : 'Invitado'}</p>

{avisos.length > 0
  ? <span class="rojo">{avisos.length} avisos</span>
  : <span class="verde">Todo en orden</span>}

El && aprovecha que false, null y undefined no pintan nada: si la condición es falsa, la expresión completa vale false y Astro no emite nada; si es verdadera, vale el elemento de la derecha y lo pinta. El ternario, en cambio, siempre elige una de sus dos ramas, así que encaja cuando de verdad hay una alternativa que mostrar en lugar de un simple aparecer-o-no.

Un aviso de estilo cierra el punto: anidar ternarios dentro de la plantilla —un ternario cuya rama es otro ternario— se vuelve ilegible enseguida. Cuando una decisión tiene más de dos ramas, la señal es clara: súbela al frontmatter, resuélvela allí con un if o un switch de verdad —que en la zona de sentencias sí caben— y deja en la plantilla una sola variable ya calculada. La plantilla gana cuando expresa qué se muestra, no el árbol de decisiones que llevó a ello.

La trampa del 0

Aquí reaparece la peculiaridad de la lección anterior, y merece una sección propia porque muerde a mucha gente. false, null y undefined se borran del HTML, pero el número 0 pinta su “0” como texto. Por eso una guarda como {avisos.length && ...} es una bomba silenciosa: si el array está vacío, length vale 0, la expresión entera vale 0, y Astro escribe un “0” suelto en la página en lugar de no escribir nada.

{/* trampa: pinta un 0 cuando la lista esta vacia */}
{avisos.length && <ul>...</ul>}

{/* correcto: la guarda es un booleano de verdad */}
{avisos.length > 0 && <ul>...</ul>}

La solución es convertir la guarda en un booleano genuino —length > 0, Boolean(x), !!x— para que el lado izquierdo sea siempre true o false, nunca 0. Es exactamente el mismo desliz que en JSX y por la misma razón: el cero es un valor falsy pero visible. Interiorizar que estás manipulando los valores crudos de JavaScript, sin una capa que los interprete por ti, es la mejor vacuna contra este bug.

flowchart LR
ARR[array de datos] --> MAP[map produce nodos hermanos]
MAP --> CAT[Astro los concatena]
CAT --> HTML[HTML emitido una vez]
COND[condicion booleana] --> GATE[operador and o ternario]
GATE --> HTML
style ARR fill:#89b4fa,color:#11111b
style HTML fill:#a6e3a1,color:#11111b
Renderizar una lista es proyectar, no vincular

El tema oculto de esta lección no es .map ni el ternario —que son JavaScript de toda la vida— sino lo que su uso revela sobre la naturaleza de Astro. En un framework reactivo, una lista es un vínculo vivo: el array y el DOM permanecen atados, y cuando añades un elemento la maquinaria de reconciliación calcula el cambio mínimo y lo aplica, usando las keys como identidad para no rehacer de más. Toda esa danza —DOM virtual, diffing, keys— existe para mantener sincronizados dos mundos que cambian con el tiempo. Astro elige no tener ese problema. Su lista no es un vínculo sino una proyección: el array se recorre una vez, cada elemento se convierte en marcado, se concatena y se emite; después, el array y el HTML dejan de conocerse. Por eso las keys sobran y por eso no hay aviso que las reclame: no son una buena práctica que Astro haya olvidado imponer, son el fósil de un mecanismo —la reconciliación— que Astro sencillamente no tiene. La misma lectura vale para los condicionales. Un && en la plantilla no vigila una condición a la espera de que cambie; se evalúa una vez en el servidor y decide, de una vez para siempre, si ese trozo de HTML existe o no. La trampa del 0 es el recordatorio más honesto de todo esto: te obliga a recordar que manipulas los valores crudos de JavaScript sin una capa que medie por ti, porque debajo de la plantilla no hay un runtime que interprete tus intenciones, solo hay concatenación de cadenas. Aprender a renderizar listas y condiciones en Astro es, en el fondo, aprender a pensar en términos de una sola pasada: no describes cómo reacciona la interfaz al cambio, describes qué HTML resulta de estos datos, esta vez.

⚔️ Proyecta colecciones y decisiones
  1. Mapea un array de objetos a una lista mostrando dos campos por elemento; encadena un .filter antes del .map y confirma el resultado en el HTML.
  2. Añade una key a cada elemento y comprueba en el HTML final que no aparece: Astro no la necesita ni la emite.
  3. Escribe una guarda con {lista.length && ...} sobre una lista vacía y observa el “0” fantasma; corrígela con > 0 y verifica que desaparece.
  4. Sustituye un bloque && por un ternario que muestre un mensaje alternativo cuando la condición sea falsa, y razona cuándo conviene cada uno.