Astro a fondo 1, componentes, props, slots y layouts

En Instalación y primer proyecto creaste un proyecto.

Un .astro tiene código de construcción entre --- y una plantilla HTML con {expresiones}.

Las props llevan datos al componente (interface Props y Astro.props); los slots, HTML.

Un layout es el documento común; cada página lo usa y llena su <slot />.

Los <style> de un componente tienen ámbito; lo global va en un CSS importado en el layout.

  • En Instalación y primer proyecto creaste un proyecto. En estas cuatro páginas montas un portafolio completo, el de Lucía Prieto: una portada con los proyectos, una página por proyecto generada a partir de ficheros Markdown, imágenes optimizadas, transiciones entre páginas y publicación en GitHub Pages. Todo el código está probado con Astro 7.4 (así en noviembre de 2026; puede haber cambiado).
  • Astro 7 necesita Node.js 22.23.4 o superior; las versiones impares (23, 25) no valen. Con la LTS (Node.js 26) no hay problema.
  • npm create astro@latest portafolio cd portafolio npm run dev
  • El asistente pregunta qué plantilla usar (elige la mínima, minimal), si instala las dependencias y si crea un repositorio de Git. La web queda en http://localhost:4321 y se recarga al guardar.
  • portafolio/ ├── public/ ← se copia tal cual: favicon, robots.txt, CNAME ├── src/ │ ├── assets/ ← imágenes que Astro optimiza │ ├── components/ ← piezas: cabecera, tarjeta… │ ├── content/ ← los textos (Markdown) de los proyectos │ ├── layouts/ ← la plantilla común de las páginas │ ├── pages/ ← cada fichero es una página │ ├── styles/ ← CSS global │ └── content.config.ts ├── astro.config.mjs └── package.json

Introducción

En Instalación y primer proyecto creaste un proyecto. En estas cuatro páginas montas un portafolio completo, el de Lucía Prieto: una portada con los proyectos, una página por proyecto generada a partir de ficheros Markdown, imágenes optimizadas, transiciones entre páginas y publicación en GitHub Pages. Todo el código está probado con Astro 7.4 (así en noviembre de 2026; puede haber cambiado).

Crear el proyecto

Astro 7 necesita Node.js 22.23.4 o superior; las versiones impares (23, 25) no valen. Con la LTS (Node.js 26) no hay problema.

npm create astro@latest portafolio cd portafolio npm run dev

El asistente pregunta qué plantilla usar (elige la mínima, minimal), si instala las dependencias y si crea un repositorio de Git. La web queda en http://localhost:4321 y se recarga al guardar.

Las carpetas

portafolio/ ├── public/ ← se copia tal cual: favicon, robots.txt, CNAME ├── src/ │ ├── assets/ ← imágenes que Astro optimiza │ ├── components/ ← piezas: cabecera, tarjeta… │ ├── content/ ← los textos (Markdown) de los proyectos │ ├── layouts/ ← la plantilla común de las páginas │ ├── pages/ ← cada fichero es una página │ ├── styles/ ← CSS global │ └── content.config.ts ├── astro.config.mjs └── package.json

La única carpeta obligatoria es src/pages/. El resto son costumbre, pero conviene seguirla: es la que vas a encontrar en cualquier proyecto.

Un componente por dentro

Un fichero .astro tiene dos partes. Entre las dos líneas --- va el código que se ejecuta al construir la web (en tu ordenador o en el servidor, nunca en el navegador). Debajo, la plantilla: HTML con expresiones entre llaves.

--- // src/components/Cabecera.astro const enlaces = [ { texto: 'Proyectos', href: '/' }, { texto: 'Sobre mí', href: '/sobre-mi' }, ]; const actual = Astro.url.pathname; --- <header> <a class="logo" href="/">Lucía Prieto</a> <nav aria-label="Principal"> <ul> {enlaces.map(({ texto, href }) => ( <li> <a href={href} aria-current={actual === href ? 'page' : undefined}>{texto}</a> </li> ))} </ul> </nav> </header>

{enlaces.map(...)} repite el <li> por cada elemento de la lista. Un atributo con valor undefined no se escribe: el enlace de la página actual lleva aria-current="page" y los demás, nada. Astro.url.pathname es la ruta de la página que se está generando.

Para mostrar algo solo si se cumple una condición, &&:

Props: datos que recibe el componente

Una tarjeta de proyecto recibe el título y la imagen desde fuera. En el componente se declaran con una interfaz Props (así el editor te avisa si te falta alguno) y se leen de Astro.props:

--- // src/components/Tarjeta.astro interface Props { titulo: string; enlace: string; etiqueta?: string; // el ? la hace opcional } const { titulo, enlace, etiqueta = 'Proyecto' } = Astro.props; --- <article class="tarjeta"> <p class="etiqueta">{etiqueta}</p> <h2><a href={enlace}>{titulo}</a></h2> </article>

Y se usa como una etiqueta HTML, después de importarla:

--- import Tarjeta from '../components/Tarjeta.astro'; --- <Tarjeta titulo="Cartel para un festival de jazz" enlace="/proyectos/cartel-festival-jazz" /> <Tarjeta titulo="Identidad de una cafetería" enlace="/proyectos/marca-cafeteria" etiqueta="Marca" />

Slots: el hueco para el contenido

Las props son datos sueltos; un slot es un hueco donde va el HTML que escribes entre la etiqueta de apertura y la de cierre. Un componente puede tener uno sin nombre y otros con nombre, y cada uno puede traer un contenido por defecto:

--- // src/components/Aviso.astro --- <aside class="aviso"> <slot name="titulo"><strong>Aviso</strong></slot> <slot /> </aside>

<Aviso> <strong slot="titulo">Disponible para encargos</strong> <p>Desde enero de 2027.</p> </Aviso>

La plantilla común (layout)

Un layout es un componente con el documento entero: <html>, <head>, la cabecera y el pie. Cada página lo usa y pone su contenido en el slot.

--- // src/layouts/Base.astro import Cabecera from '../components/Cabecera.astro'; import '../styles/global.css'; interface Props { titulo: string; descripcion?: string; } const { titulo, descripcion = 'Portafolio de Lucía Prieto, diseñadora gráfica' } = Astro.props; --- <!doctype html> <html lang="es"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="description" content={descripcion} /> <title>{titulo} · Lucía Prieto</title> </head> <body> <Cabecera /> <main> <slot /> </main> <footer> <slot name="pie">© 2026 Lucía Prieto</slot> </footer> </body> </html>

--- // src/pages/sobre-mi.astro import Base from '../layouts/Base.astro'; --- <Base titulo="Sobre mí"> <h1>Sobre mí</h1> <p>Diseño carteles, marcas y webs desde Sevilla.</p> <p slot="pie">Escríbeme: hola@ejemplo.es</p> </Base>

Estilos con ámbito

Un <style> dentro de un componente solo afecta a ese componente: Astro añade un atributo propio (data-astro-cid-…) a sus etiquetas y a sus selectores. Si escribes h2 { … } en la tarjeta, los h2 del resto de la web no cambian.

<style> .tarjeta { border: 1px solid #ddd; border-radius: 0.5rem; } h2 { font-size: 1.25rem; } </style>

ParaEscribe Estilos de todo el sitio (variables, body, tipografía)Un CSS en src/styles/ importado en el layout Un <style> sin ámbito<style is:global> Llegar a lo que viene de fuera (el HTML de un Markdown, un slot)article :global(h2) { … }

Practica

Crea el proyecto con la plantilla mínima y arranca npm run dev. Haz el layout Base.astro y úsalo en index.astro y en una página nueva sobre-mi.astro. Crea Cabecera.astro con el menú y la marca de la página actual. Crea Tarjeta.astro con props y pon tres tarjetas en la portada. Dale estilo a la tarjeta con un <style> en el componente y comprueba en las DevTools el atributo data-astro-cid-….

Errores frecuentes

Usar document o window entre los ---: ese código no se ejecuta en el navegador. Llamar tarjeta.astro al componente y usarlo como <tarjeta>: tiene que empezar por mayúscula. Dejar una etiqueta sin cerrar, como un <p> sin su </p>. Desde Astro 7 es un error al construir, no algo que se arregla solo. Escribir el CSS de todo el sitio en el <style> de un componente: solo se aplica dentro de él.

Astro
16

Astro a fondo 1, componentes, props, slots y layouts

En Instalación y primer proyecto creaste un proyecto.

Código del tema: const { titulo } = Astro.props;

📘 Teoría

Introducción

En Instalación y primer proyecto creaste un proyecto. En estas cuatro páginas montas un portafolio completo, el de Lucía Prieto: una portada con los proyectos, una página por proyecto generada a partir de ficheros Markdown, imágenes optimizadas, transiciones entre páginas y publicación en GitHub Pages. Todo el código está probado con Astro 7.4 (así en noviembre de 2026; puede haber cambiado).

Crear el proyecto

Astro 7 necesita Node.js 22.23.4 o superior; las versiones impares (23, 25) no valen. Con la LTS (Node.js 26) no hay problema.

npm create astro@latest portafolio
cd portafolio
npm run dev

El asistente pregunta qué plantilla usar (elige la mínima, minimal), si instala las dependencias y si crea un repositorio de Git. La web queda en http://localhost:4321 y se recarga al guardar.

Las carpetas

portafolio/
├── public/              ← se copia tal cual: favicon, robots.txt, CNAME
├── src/
│   ├── assets/          ← imágenes que Astro optimiza
│   ├── components/      ← piezas: cabecera, tarjeta…
│   ├── content/         ← los textos (Markdown) de los proyectos
│   ├── layouts/         ← la plantilla común de las páginas
│   ├── pages/           ← cada fichero es una página
│   ├── styles/          ← CSS global
│   └── content.config.ts
├── astro.config.mjs
└── package.json

La única carpeta obligatoria es src/pages/. El resto son costumbre, pero conviene seguirla: es la que vas a encontrar en cualquier proyecto.

Un componente por dentro

Un fichero .astro tiene dos partes. Entre las dos líneas --- va el código que se ejecuta al construir la web (en tu ordenador o en el servidor, nunca en el navegador). Debajo, la plantilla: HTML con expresiones entre llaves.

---
// src/components/Cabecera.astro
const enlaces = [
  { texto: 'Proyectos', href: '/' },
  { texto: 'Sobre mí', href: '/sobre-mi' },
];
const actual = Astro.url.pathname;
---
<header>
  <a class="logo" href="/">Lucía Prieto</a>
  <nav aria-label="Principal">
    <ul>
      {enlaces.map(({ texto, href }) => (
        <li>
          <a href={href} aria-current={actual === href ? 'page' : undefined}>{texto}</a>
        </li>
      ))}
    </ul>
  </nav>
</header>
  • {enlaces.map(...)} repite el <li> por cada elemento de la lista.
  • Un atributo con valor undefined no se escribe: el enlace de la página actual lleva aria-current="page" y los demás, nada.
  • Astro.url.pathname es la ruta de la página que se está generando.

Para mostrar algo solo si se cumple una condición, &&:

{etiquetas.length > 0 && <ul>{etiquetas.map((e) => <li>{e}</li>)}</ul>}

Props: datos que recibe el componente

Una tarjeta de proyecto recibe el título y la imagen desde fuera. En el componente se declaran con una interfaz Props (así el editor te avisa si te falta alguno) y se leen de Astro.props:

---
// src/components/Tarjeta.astro
interface Props {
  titulo: string;
  enlace: string;
  etiqueta?: string;    // el ? la hace opcional
}

const { titulo, enlace, etiqueta = 'Proyecto' } = Astro.props;
---
<article class="tarjeta">
  <p class="etiqueta">{etiqueta}</p>
  <h2><a href={enlace}>{titulo}</a></h2>
</article>

Y se usa como una etiqueta HTML, después de importarla:

---
import Tarjeta from '../components/Tarjeta.astro';
---
<Tarjeta titulo="Cartel para un festival de jazz" enlace="/proyectos/cartel-festival-jazz" />
<Tarjeta titulo="Identidad de una cafetería" enlace="/proyectos/marca-cafeteria" etiqueta="Marca" />

El nombre del componente empieza por mayúscula. Con minúscula, Astro lo trata como una etiqueta HTML.

Slots: el hueco para el contenido

Las props son datos sueltos; un slot es un hueco donde va el HTML que escribes entre la etiqueta de apertura y la de cierre. Un componente puede tener uno sin nombre y otros con nombre, y cada uno puede traer un contenido por defecto:

---
// src/components/Aviso.astro
---
<aside class="aviso">
  <slot name="titulo"><strong>Aviso</strong></slot>
  <slot />
</aside>
<Aviso>
  <strong slot="titulo">Disponible para encargos</strong>
  <p>Desde enero de 2027.</p>
</Aviso>

La plantilla común (layout)

Un layout es un componente con el documento entero: <html>, <head>, la cabecera y el pie. Cada página lo usa y pone su contenido en el slot.

---
// src/layouts/Base.astro
import Cabecera from '../components/Cabecera.astro';
import '../styles/global.css';

interface Props {
  titulo: string;
  descripcion?: string;
}

const { titulo, descripcion = 'Portafolio de Lucía Prieto, diseñadora gráfica' } = Astro.props;
---
<!doctype html>
<html lang="es">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="description" content={descripcion} />
    <title>{titulo} · Lucía Prieto</title>
  </head>
  <body>
    <Cabecera />
    <main>
      <slot />
    </main>
    <footer>
      <slot name="pie">© 2026 Lucía Prieto</slot>
    </footer>
  </body>
</html>
---
// src/pages/sobre-mi.astro
import Base from '../layouts/Base.astro';
---
<Base titulo="Sobre mí">
  <h1>Sobre mí</h1>
  <p>Diseño carteles, marcas y webs desde Sevilla.</p>
  <p slot="pie">Escríbeme: hola@ejemplo.es</p>
</Base>

Estilos con ámbito

Un <style> dentro de un componente solo afecta a ese componente: Astro añade un atributo propio (data-astro-cid-…) a sus etiquetas y a sus selectores. Si escribes h2 { … } en la tarjeta, los h2 del resto de la web no cambian.

<style>
  .tarjeta { border: 1px solid #ddd; border-radius: 0.5rem; }
  h2 { font-size: 1.25rem; }
</style>
ParaEscribe
Estilos de todo el sitio (variables, body, tipografía)Un CSS en src/styles/ importado en el layout
Un <style> sin ámbito<style is:global>
Llegar a lo que viene de fuera (el HTML de un Markdown, un slot)article :global(h2) { … }

Practica

  1. Crea el proyecto con la plantilla mínima y arranca npm run dev.
  2. Haz el layout Base.astro y úsalo en index.astro y en una página nueva sobre-mi.astro.
  3. Crea Cabecera.astro con el menú y la marca de la página actual.
  4. Crea Tarjeta.astro con props y pon tres tarjetas en la portada.
  5. Dale estilo a la tarjeta con un <style> en el componente y comprueba en las DevTools el atributo data-astro-cid-….

Errores frecuentes

  • Usar document o window entre los ---: ese código no se ejecuta en el navegador.
  • Llamar tarjeta.astro al componente y usarlo como <tarjeta>: tiene que empezar por mayúscula.
  • Dejar una etiqueta sin cerrar, como un <p> sin su </p>. Desde Astro 7 es un error al construir, no algo que se arregla solo.
  • Escribir el CSS de todo el sitio en el <style> de un componente: solo se aplica dentro de él.

¿Qué es esto?

Soy Cristian Eslava y a veces hago webs para procrastinar yo y vosotros. culTest

La hice en febrero de 2026 para facilitar el aprendizaje de mis alumnos. La idea es aprender desarrollo web practicando y que el proyecto siga creciendo con nuevos temas, tests y retos.

Está inspirada en MDN, W3Schools, CodePen, Manz y muchos otros sitios de documentación sobre desarrollo web. Quería combinar teoría útil, ejemplos ejecutables, retos y el sistema de tests que ya tenía en culTest. culTest

Si te gustó, si no te gustó o si quieres escribirme, puedes hacerlo en cristianeslava@gmail.com