Saltearse al contenido

Componentes

Los componentes de Astro son los bloques de construcción básicos de cualquier proyecto de Astro. Son componentes de maquetado HTML sin tiempo de ejecución en el lado del cliente. Puedes identificar un componente de Astro por su extensión de archivo: .astro.

Los componentes de Astro son extremadamente flexibles. Un componente de Astro puede contener UI reutilizable, tal como encabezados o una tarjeta de perfil. También puede contener un fragmento pequeño de HTML, o una colección de etiquetas <meta> para facilitar nuestro trabajo con el SEO. Los componentes de Astro también pueden contener el layout de una página.

La más importante que debes saber sobre los componentes de Astro es que no se renderizan en el cliente. Se renderizan a HTML ya sea en tiempo de compilación o bajo demanda utilizando renderizado del lado del servidor (SSR). Puedes incluir código JavaScript dentro del frontmatter de tu componente y todo el código será eliminado de la página final enviada a los navegadores de tus usuarios. El resultado es un sitio más rápido, con cero huella de JavaScript agregada por defecto.

Cuando tu componente Astro necesite interactividad del lado del cliente, puedes agregar etiquetas <script> HTML estándar o componentes de UI Framework.

Un componente de Astro se compone de dos partes principales: el script del componente y el maquetado del componente. Cada parte cumple una función diferente, pero juntas proveen un marco de trabajo más fácil de utilizar y lo suficientemente expresivo para manejar cualquier cosa que desees construir.

src/components/Componente.astro
---
// Script del componente (JavaScript)
---
<!-- Maquetado del componente (HTML + Expresiones JS) -->

Astro utiliza una valla de código (---) para identificar el script del componente Astro. Si has escrito Markdown anteriormente deberías estar familiarizado con un concepto similar llamado frontmatter. El script del componente de Astro fue inspirado por este concepto.

Puedes utilizar el script del componente para escribir cualquier código de JavaScript que necesites para renderizar el maquetado. Esto puede incluir:

  • Importar otros componentes Astro
  • Importar componentes de otros frameworks, como React
  • Importar datos, como un archivo JSON
  • Consultar contenido de una API o base de datos
  • Crear variables que luego puedes referenciar en tu maquetado
src/components/MiComponente.astro
---
import UnComponenteAstro from '../components/UnComponenteAstro.astro';
import UnComponenteReact from '../components/UnComponenteReact.jsx';
import algunosDatos from '../data/pokemon.json';
// Acceder a props recibidas, por ejemplo `<X title="Hola Mundo" />`
const { title } = Astro.props;
// Consultar datos externos, de una API privada o base de datos
const datos = await fetch('API_URL_SECRETA/users').then(r => r.json());
---
<!-- ¡Tu maquetado va aquí! -->

La valla de código está diseñada para garantizar que el código JavaScript que escribes adentro se encuentre “encapsulado”. Este código no se filtrará a tu aplicación, o llegará al usuario final. Puedes escribir código que sea costoso o sensible (como una llamada a la base de datos) sin preocuparte por que estos datos sensibles lleguen al navegador del usuario.

El maquetado del componente se encuentra debajo de la valla de código y determina la salida HTML de tu componente.

Si escribes solo HTML en esta sección, el componente va a renderizar este HTML en cualquier página Astro donde sea importado o utilizado.

Sin embargo, el maquetado de componentes de Astro también soporta expresiones de JavaScript, etiquetas <style> y <script> de Astro, componentes importados, y directivas especiales de Astro. Los datos y valores definidos en el script del componente pueden ser usados en el maquetado del componente para producir HTML creado dinámicamente.

src/components/MyFavoritePokemon.astro
---
// El script de tu componente va aquí!
import Banner from '../components/Banner.astro';
import ReactPokemonComponent from '../components/ReactPokemonComponent.jsx';
const misPokemonesFavoritos = [/* ... */];
const { title } = Astro.props;
---
<!-- ¡Soporta comentarios HTML! -->
{/* La sintaxis de comentario de JS es también válida */}
<h1>¡Hola mundo!</h1>
<!-- Utiliza props y otras variables definidas en el script del componente: -->
<p>{title}</p>
<!-- Incluye otros componentes UI con la directiva de hidratación `client:`: -->
<ReactPokemonComponent client:visible />
<!-- Puedes mezclar HTML con expresiones de JavaScript, similar a JSX: -->
<ul>
{misPokemonesFavoritos.map((data) => <li>{data.name}</li>)}
</ul>
<!-- ¡Usa una directiva de maquetado para crear nombres de clase a partir de múltiples strings o incluso objetos! -->
<p class:list={["agregar", "dinámico", {classNames: true}]} />

Los componentes son diseñados para ser reusables y componibles. Puedes usar componentes dentro de otros componentes para construir UI más y más avanzada. Por ejemplo, un componente Button podría ser usado para crear un componente ButtonGroup:

src/components/ButtonGroup.astro
---
import Button from './Button.astro';
---
<div>
<Button title="Botón 1" />
<Button title="Botón 2" />
<Button title="Botón 3" />
</div>

Un componente de Astro puede definir y aceptar props. Estas props estarán disponibles para ser utilizadas en el renderizado del maquetado HTML y además estarán disponibles en el script del componente de manera global dentro del objeto Astro.props.

Aquí vemos un ejemplo de un componente que recibe una prop saludo y otra nombre. Fíjate que las props a recibir están desestructuradas del objeto global Astro.props.

src/components/GreetingHeadline.astro
---
// Utilización: <GreetingHeadline saludo="Qué tal" nombre="Amiga" />
const { saludo, nombre } = Astro.props
---
<h2>{saludo}, {nombre}!</h2>

Este componente, cuando se importa y renderiza en otros componentes, layouts o páginas de Astro, puede pasar estas props como atributos:

src/components/GreetingCard.astro
---
import GreetingHeadline from './GreetingHeadline.astro';
const name = 'Astro';
---
<h1>Tarjeta de saludos</h1>
<GreetingHeadline greeting="Hola" name={name} />
<p>¡Espero que tenga un día maravilloso!</p>

También puedes definir props con TypeScript usando una interfaz de tipo Props. Astro recogerá automáticamente la interfaz Props en el frontmatter y dará advertencias/errores de tipo para tu proyecto. A estas propiedades también se les puede dar valores predeterminados cuando se desestructuran desde Astro.props.

src/components/GreetingHeadline.astro
---
interface Props {
nombre: string;
saludo?: string;
}
const { saludo = "Hola", nombre } = Astro.props;
---
<h2>{saludo}, {nombre}!</h2>

A los props de componentes se les pueden dar valores predeterminados para usar cuando no se proporciona ninguno.

src/components/GreetingHeadline.astro
---
const { greeting = "Hola", name = "Astronauta" } = Astro.props;
---
<h2>¡{greeting}, {name}!</h2>

El elemento <slot /> es un espacio reservado para contenido HTML externo, permitiéndote inyectar (o ingresar en la “ranura”) elementos hijos provenientes de otros archivos en el maquetado de tu componente.

Por defecto, todos los elementos hijos que le sean enviados a un componente serán renderizados en su <slot />.

src/components/Wrapper.astro
---
import Header from './Header.astro';
import Logo from './Logo.astro';
import Footer from './Footer.astro';
const { titulo } = Astro.props;
---
<div id="content-wrapper">
<Header />
<Logo />
<h1>{titulo}</h1>
<slot /> <!-- aquí van los hijos -->
<Footer />
</div>
src/pages/fred.astro
---
import Wrapper from '../components/Wrapper.astro';
---
<Wrapper titulo="Página de Fred">
<h2>Todo sobre Fred</h2>
<p>Aquí veremos cosas sobre Fred.</p>
</Wrapper>

Este patrón es la base de un maquetado de componente Astro: todo el contenido HTML de una página puede ser “envuelto” con etiquetas <SomeLayoutComponent></SomeLayoutComponent> y enviadas al componente para que se renderice dentro de elementos comunes de página definidos allí.

Un componente de Astro también puede tener slots con nombre. Esto te permite compartir elementos HTML únicamente con el nombre correspondiente al slot.

Los slots se nombran usando el atributo name:

src/components/Wrapper.astro
---
import Header from './Header.astro';
import Logo from './Logo.astro';
import Footer from './Footer.astro';
const { titulo } = Astro.props;
---
<div id="content-wrapper">
<Header />
<slot name="after-header" /> <!-- hijos con el atributo `slot="after-header"` van aquí -->
<Logo />
<h1>{titulo}</h1>
<slot /> <!-- hijos sin `slot`, o con el atributo `slot="default"` van aquí -->
<Footer />
<slot name="after-footer" /> <!-- hijos con el atributo `slot="after-footer"` van aquí -->
</div>

Para inyectar contenido HTML en un slot particular, usa el atributo slot en cualquier elemento hijo para especificar el nombre del slot. Todos los demás elementos hijos del componente serán inyectados en el slot por defecto (sin nombre) <slot />.

src/pages/fred.astro
---
import Wrapper from '../components/Wrapper.astro';
---
<Wrapper titulo="Página de Fred">
<img src="https://my.photo/fred.jpg" slot="after-header" />
<h2>Todo sobre Fred</h2>
<p>Aquí veremos cosas sobre Fred.</p>
<p slot="after-footer">Copyright 2022</p>
</Wrapper>

Para pasar múltiples elementos HTML dentro del placeholder <slot/> de un componente sin un <div> envolvente, usa el atributo slot="" en el componente <Fragment/> de Astro:

src/components/CustomTable.astro
---
// Crea una tabla personalizada con placeholders de slots con nombre para el contenido del header y el body
---
<table class="bg-white">
<thead class="sticky top-0 bg-white"><slot name="header" /></thead>
<tbody class="[&_tr:nth-child(odd)]:bg-gray-100"><slot name="body" /></tbody>
</table>

Inyecta múltiples filas y columnas de contenido HTML usando el atributo slot="" para especificar el contenido "header" y "body". Los elementos HTML individuales también pueden ser estilizados:

src/components/StockTable.astro
---
import CustomTable from './CustomTable.astro';
---
<CustomTable>
<Fragment slot="header"> <!-- agrega un encabezado para la tabla -->
<tr><th>Nombre del producto</th><th>Unidades disponibles</th></tr>
</Fragment>
<Fragment slot="body"> <!-- pasar el cuerpo de la tabla -->
<tr><td>Chanclas</td><td>64</td></tr>
<tr><td>Botas</td><td>32</td></tr>
<tr><td>Tenis</td><td class="text-red-500">0</td></tr>
</Fragment>
</CustomTable>

Ten en cuenta que los slots con nombre deben ser hijos inmediatos del componente. No puedes pasar slots con nombre a través de elementos anidados.

Los slots también pueden renderizar contenido alternativo en el caso que no reciban datos con <slot /> para emparejar, sea slot con nombre o no.

src/components/Wrapper.astro
---
import Header from './Header.astro';
import Logo from './Logo.astro';
import Footer from './Footer.astro';
const { titulo } = Astro.props;
---
<div id="content-wrapper">
<Header />
<Logo />
<h1>{titulo}</h1>
<slot>
<p>Este es mi contenido alternativo, si no hay ningún elemento pasado al slot</p>
</slot>
<Footer />
</div>

El contenido de reserva solo se mostrará cuando no se pasen elementos coincidentes con el atributo slot="nombre" a un slot con nombre.

Astro pasará un slot vacío cuando un elemento slot exista pero no tenga contenido para pasar. El contenido de reserva no puede ser usado como un valor predeterminado cuando se pasa un slot vacío. El contenido de reserva solo se mostrará cuando no se pueda encontrar un elemento slot.

Los slots pueden ser transferidos a otros componentes. Por ejemplo, cuando se crean plantillas anidadas:

src/layouts/BaseLayout.astro
---
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<slot name="head" />
</head>
<body>
<slot />
</body>
</html>
src/layouts/HomeLayout.astro
---
import BaseLayout from './BaseLayout.astro';
---
<BaseLayout>
<slot name="head" slot="head" />
<slot />
</BaseLayout>

Ahora, los slots por defecto y head pasados a HomeLayout serán transferidos al componente padre BaseLayout.

src/pages/index.astro
---
import HomeLayout from '../layouts/HomeLayout.astro';
---
<HomeLayout>
<title slot="head">Astro</title>
<h1>Astro</h1>
</HomeLayout>

Astro soporta importar y usar archivos .html como componentes o colocarlos dentro del subdirectorio src/pages/. Es posible que quieras usar componentes HTML si estás reusando código de un sitio existente construido sin usar frameworks, o si quieres asegurarte que tu componente no tiene características dinámicas.

Los componentes HTML solo deben contener HTML válido, y por lo tanto le faltarán características claves de los componentes de Astro.

  • Ellos no soportan el frontmatter, importaciones del lado del servidor, o expresiones dinámicas.
  • Cualquier etiqueta <script> quedan sin agrupar, son tratados como si tuvieran in:inline
  • Ellos solo pueden referenciar recursos que están en la carpeta public/.
Aprende sobre el uso de componentes de frameworks UI en tu proyecto Astro.