Componentes

Los componentes de Astro son los bloques fundamentales para cualquier proyecto de Astro. Están compuestos únicamente por HTML y no poseen ejecución del lado del cliente.

Si sabes HTML, ya sabes lo suficiente para escribir tu primer componente Astro.

La sintaxis del componente de Astro es un superconjunto de HTML. Fue diseñada para sea familiar para alguien con experiencia en HTML o JSX (EN), además es compatible con componentes y expresiones de JavaScript. Puedes identificar los componentes Astro por su extensión: .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.

Lo más importante acerca de los componentes de Astro es que se renderizan a HTML durante la construccion del proyecto. Aún si posees código JavaScript dentro de tus componentes, este código solo se ejecuta al construir tu projecto, siendo removido de la página final que se enviará al usuario. El resultado es un sitio web más rápido y sin rastros de JavaScript.

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) -->

Puedes utilizar componentes dentro de otros componentes para construir una UI más avanzada y compleja. Por ejemplo, el componente Button puede ser utilizado para crear un componente ButtonGroup de la siguiente manera:

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

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.

Debajo del script del componente vive el maquetado. El maquetado del componente define el HTML que generará 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, la sintaxis de maquetado del componente de Astro también es compatible con expresiones de JavaScript, componentes importados y directivas especiales de Astro. Los datos y valores definidos (en la construcción la página) en el script del componente pueden ser utilizados en el maquetado del componente para producir HTML creado dinámicamente.

src/components/MyFavoritePokemon.astro
---
// El script de tu componente va aquí!
import ReactPokemonComponent from '../components/ReactPokemonComponent.jsx';
const misPokemonesFavoritos = [/* ... */];
---
<!-- Soporta comentarios HTML! -->

<h1>Hola mundo!</h1>

<!-- Utiliza props y otras variables definidas en el script del componente: -->
<p>Mi pokemon favorito es: {Astro.props.title}</p>

<!-- Incluye otros componentes 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>

<!-- ¡Use 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}]} />

Puedes definir variables locales de JavaScript dentro del script del componente de Astro. ¡Luego puedes inyectar estas variables en el maquetado del componente usando expresiones similares a JSX!

Las variables locales pueden ser agregadas al maquetado usando la sintaxis de llaves:

src/components/Variables.astro
---
const nombre = "Astro";
---
<div>
  <h1>Hola {nombre}!</h1> <!-- <h1>Hola Astro!</h1> -->
</div>

Las variables locales pueden ser utilizadas entre llaves para pasar valores a atributos de componentes y elementos HTML:

src/components/DynamicAttributes.astro
---
const nombre = "Astro";
---
<h1 class={nombre}>Soporta expresiones en atributos</h1>

<MiComponente nombreDeAtributo={`MiNombreEs${nombre}`} />

Las variables locales pueden ser utilizadas en funciones similares a JSX para producir elementos HTML dinámicos:

src/components/DynamicHtml.astro
---
const items = ["Perro", "Gato", "Mono"];
---
<ul>
  {items.map((item) => (
    <li>{item}</li>
  ))}
</ul>

Astro puede mostrar HTML de forma condicional utilizando operadores lógicos y expresiones ternarias en JSX.

src/components/ConditionalHtml.astro
---
const visible = true;
---
{visible && <p>¡Muéstrame!</p>}

{visible ? <p>¡Muéstrame!</p> : <p>¡O muéstrame a mi!</p>}

También puedes usar etiquetas dinámicas para asignar a una variable el nombre de una etiqueta HTML o un componente importado.

src/components/DynamicTags.astro
---
import MyComponent from "./MyComponent.astro";
const Element = 'div'
const Component = MyComponent;
---
<Element>Hola!</Element> <!-- es renderizado como <div>Hola!</div> -->
<Component /> <!-- es renderizado como <MyComponent /> -->

Al usar etiquetas dinámicas:

Los nombres de las variables deben estar en mayúscula. Por ejemplo, usar Element, no element. De lo contrario, Astro intentará renderizar el nombre de la variable como una etiqueta literal de HTML.

No admiten directivas de hidratación. Al usar client:* directiva de hidratación, Astro necesita saber cuáles son los componentes que se deben empaquetar para producción, y el patrón de etiqueta dinámica previene que esto funcione.

Fragmentos & elementos múltiples

Section titled Fragmentos & elementos múltiples

Una plantilla de componente de Astro puede renderizar múltiples elementos sin necesidad de envolver todo en un solo <div> o <>, a diferencia de JavaScript o JSX.

src/components/RootElements.astro
---
// Maquetado con múltiples elementos
---
<p>No es necesario envolver elementos en un solo elemento contenedor.</p>
<p>Astro es compatible con el uso de múltiples elementos en la raíz del maquetado</p>

Sin embargo, al utilizar las expresiones para crear elementos dinámicamente, debes envolver estos elementos dentro de un Fragment de la misma forma que lo harías utilizando JavaScript o JSX. Astro es compatible con el uso de <Fragment> </Fragment> o su abreviación <> </>.

---
const items = ["Perro", "Gato", "Mono"];
---
<ul>
  {items.map((item) => (
    <>
      <li>{item} Rojo</li>
      <li>{item} Azul</li>
      <li>{item} Verde</li>
    </>
  ))}
</ul>

Los fragmentos también pueden ser útiles para evitar envolver componentes al usar las directivas set:*, como en el siguiente ejemplo:

src/components/SetHtml.astro
---
const htmlString = '<p>Contenido HTML sin procesar</p>';
---
<Fragment set:html={htmlString} />

La sintaxis del componente Astro es un superconjunto de HTML. Fue diseñado para que resulte familiar para alguien con experiencia en HTML o JSX, pero hay un par de diferencias clave entre los archivos .astro y JSX.

En Astro, utiliza el formato estándar kebab-case para todos los atributos HTML en lugar del camelCase que se usa en JSX. Esto incluso funciona para class, que no es compatible con React.

example.astro
<div className="box" dataValue="3" />
<div class="box" data-value="3" />

En Astro, puedes usar comentarios HTML estándar donde JSX usaría comentarios de estilo JavaScript.

example.astro
<!-- comentarios HTML estándar son válidos en archivos .astro -->

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, se pueden pasar estas props como atributos:

src/components/GreetingCard.astro
---
import GreetingHeadline from './GreetingHeadline.astro';
const name = "Astro"
---
<h1>Greeting Card</h1>
<GreetingHeadline greeting="Hi" name={name} />
<p>I hope you have a wonderful day!</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 = "Hello", name = "Astronaut" } = 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 la plantilla de página de un componente de Astro: una página entera de contenido HTML puede ser “envuelta” con etiquetas <Layout></Layout> y enviadas al componente Layout para ser renderizada dentro de elementos comunes de la página.

Un componente de Astro también puede tener slots con nombre. Esto te permite compartir elementos HTML únicamente con el nombre correspondiente al 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 />
  <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>
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>

Utiliza un atributo slot="mi-slot" en el elemento hijo que quieras enviar junto con su <slot name="mi-slot" /> emparejado en tu componente.

Contenido alternativo para slots

Section titled Contenido alternativo para slots

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>

Astro también soporta etiquetas de estilo CSS <style> dentro del maquetado del componente.

Pueden utilizarse para estilar los componentes y todas las reglas de estilo son encapsuladas en el componente para evitar conflictos de CSS en aplicaciones grandes.

src/components/StyledHeading.astro
---
// El script de tu componente va aquí!
---
<style>
  /* encapsulado en el componente, otros H1 en la página no estarán afectados */
  h1 { color: red }
</style>

<h1>Hola mundo!</h1>

📚 Lee nuestra guía de estilos para más información en cómo aplicar estilos.

Para enviar JavaScript al cliente sin utilizar componentes de framework (React, Svelte, Vue, Preact, SolidJS, AlpineJS, Lit) o integraciones de Astro (ej. astro-XElement), puedes utilizar una etiqueta <script> en el maquetado de tu componente y enviar JavaScript al navegador que será ejecutado en el ámbito global.

Por defecto, las etiquetas <script> son procesadas por Astro.

  • Cualquier importación se empaquetará, lo que le permitirá importar archivos locales o módulos de Node.
  • El script procesado se inyectará en el <head> de la página con type="module".
  • Tiene soporte completo de Typescript, incluyendo el importado de archivos Typescript.
  • Si su componente es usado varias veces en una página, la etiqueta del script solo se incluirá una vez.
<script>
  // Procesado! Comprimido! Soporta Typescript! Funciona la importación de ESM, aun si son paquetes npm.
</script>

Para evitar agrupar el script, puedes usar el atributo is:inline.

<script is:inline>
  // Será renderizado en el HTML tal cual sea escrito!
  // El importado de ESM relativos al archivo no serán resueltos.
</script>

Se pueden usar múltiples etiquetas <script> en el mismo archivo .astro usando cualquier combinación de los métodos anteriores.

📚 Lea nuestra página de referencias de directivas para más información acerca de las directivas disponibles para las etiquetas <script>.

Cuándo utilizarlo: Si tu archivo JavaScript vive dentro de la carpeta public/.

Ten en cuenta que este enfoque saltea el procesamiento, compresión y optimización de JavaScript provista por Astro. (Para aprovechar todo esto debes utilizar el método import descripto en el siguiente item)

// Ruta URL absoluta
<script is:inline src="/algun-script-externo.js"></script>

Cuándo utilizarlo: Si tu script externo vive dentro de src/ y soporta el tipo de módulos ESM.

Astro detecta los módulos JavaScript importados del lado del cliente y luego comprime, optimiza y añade el JS a la página automáticamente.

// Importar módulo ESM
<script>
  import './algun-script-externo.js';
</script>

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 freamwors, o si quieres aseguarte 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/.

📚 Lee acerca de los componentes internos de Astro.

📚 Aprende acerca del uso de componentes de framework de JavaScript en Astro.