Volver al inicio
Lección 2: Estructura del Proyecto

Estructura del Proyecto Astro

Aprende cómo se organiza un proyecto Astro y qué función cumple cada carpeta y archivo.

1. Vista General del Proyecto

Cuando creas un proyecto Astro con npm create astro@latest, obtienes una estructura organizada y clara.

mi-proyecto/
src/ ← Código fuente principal
pages/ ← Páginas y rutas
layouts/ ← Plantillas reutilizables
components/ ← Componentes UI
styles/ ← CSS global
public/ ← Archivos estáticos
astro.config.mjs ← Configuración
package.json

2. src/pages/ - Sistema de Rutas

pages/ es la carpeta más importante. Cada archivo .astro aquí se convierte automáticamente en una ruta.

Routing basado en archivos: El nombre del archivo determina la URL.

Archivo:

src/pages/index.astro

Ruta:

https://tusitio.com/

Archivo:

src/pages/blog.astro

Ruta:

https://tusitio.com/blog

Archivo:

src/pages/blog/post-1.astro

Ruta:

https://tusitio.com/blog/post-1

Archivo:

src/pages/productos/[id].astro

Ruta:

https://tusitio.com/productos/123

Ruta dinámica con parámetro

Ejemplo: src/pages/blog.astro

1---
2// Código de la página (se ejecuta en el servidor)
3const titulo = "Mi Blog";
4const posts = [
5 { id: 1, titulo: "Primer Post" },
6 { id: 2, titulo: "Segundo Post" }
7];
8---
9
10<!DOCTYPE html>
11<html lang="es">
12<head>
13 <meta charset="UTF-8">
14 <title>{titulo}</title>
15</head>
16<body>
17 <h1>{titulo}</h1>
18 <ul>
19 {posts.map(post => (
20 <li><a href={`/blog/${post.id}`}>{post.titulo}</a></li>
21 ))}
22 </ul>
23</body>
24</html>

3. src/layouts/ - Plantillas Reutilizables

Los layouts son plantillas que envuelven el contenido de tus páginas. Perfectos para headers, footers y estructura común.

DRY (Don't Repeat Yourself): Define una vez, usa en todas las páginas.

src/layouts/Base.astro

1---
2const { titulo } = Astro.props;
3---
4
5<!DOCTYPE html>
6<html lang="es">
7<head>
8 <meta charset="UTF-8">
9 <title>{titulo}</title>
10</head>
11<body>
12 <header>
13 <nav>Mi Sitio</nav>
14 </header>
15
16 <main>
17 <slot />
18 </main>
19
20 <footer>
21 © 2025
22 </footer>
23</body>
24</html>

src/pages/about.astro

1---
2import Base from '../layouts/Base.astro';
3---
4
5<Base titulo="Sobre Nosotros">
6 <h1>Acerca de</h1>
7 <p>Esta es la página sobre nosotros.</p>
8</Base>

Tip: El <slot /> es donde se inserta el contenido de la página que usa el layout.

Cómo funciona el flujo Layout + Página

PASO 1 Tienes un Layout con estructura común

src/layouts/Base.astro

<html>
<head>...</head>
<body>
<header>Mi Sitio</header>
<main>
<slot /> ← Aquí va el contenido
</main>
<footer>© 2025</footer>
</body>
</html>
PASO 2 Tu página usa el Layout y pasa contenido

src/pages/index.astro

import Base from '../layouts/Base.astro';
<Base titulo="Inicio">
<h1>Bienvenido</h1>
<p>Esta es mi página de inicio</p>
</Base>
PASO 3 Astro combina Layout + Contenido = HTML final

HTML resultante en el navegador

<html>
<head>...</head>
<body>
<header>Mi Sitio</header>
<main>
<h1>Bienvenido</h1>
<p>Esta es mi página de inicio</p>
</main>
<footer>© 2025</footer>
</body>
</html>

Flujo Visual:

Layout

Estructura común

+

Página

Contenido único

=

HTML Final

Página completa

Reutilizando el mismo Layout en múltiples páginas

src/pages/index.astro

1---
2import Base from '../layouts/Base.astro';
3---
4
5<Base titulo="Inicio">
6 <h1>Inicio</h1>
7 <p>Página principal</p>
8</Base>

Ruta: /

src/pages/blog.astro

1---
2import Base from '../layouts/Base.astro';
3---
4
5<Base titulo="Blog">
6 <h1>Blog</h1>
7 <p>Artículos recientes</p>
8</Base>

Ruta: /blog

src/pages/contact.astro

1---
2import Base from '../layouts/Base.astro';
3---
4
5<Base titulo="Contacto">
6 <h1>Contacto</h1>
7 <form>...</form>
8</Base>

Ruta: /contact

src/pages/about.astro

1---
2import Base from '../layouts/Base.astro';
3---
4
5<Base titulo="Nosotros">
6 <h1>Sobre Nosotros</h1>
7 <p>Nuestra historia</p>
8</Base>

Ruta: /about

Ventaja: Las 4 páginas comparten el mismo header, footer y estructura. Si cambias el Layout, ¡cambian todas las páginas automáticamente!

4. src/components/ - Componentes UI

Los components son piezas reutilizables de UI. Pueden ser componentes Astro (.astro) o de frameworks (React, Vue, Svelte).

Ejemplo: src/components/Card.astro

1---
2const { titulo, descripcion } = Astro.props;
3---
4
5<div class="card">
6 <h3>{titulo}</h3>
7 <p>{descripcion}</p>
8</div>
9
10<style>
11 .card {
12 border: 1px solid #ccc;
13 border-radius: 8px;
14 padding: 1rem;
15 background: white;
16 }
17</style>

Usando el componente:

1---
2import Card from '../components/Card.astro';
3---
4
5<Card
6 titulo="Astro es genial"
7 descripcion="Aprende Astro fácilmente"
8/>
9<Card
10 titulo="Componentes reutilizables"
11 descripcion="Escribe una vez, usa muchas veces"
12/>

5. public/ - Archivos Estáticos

La carpeta public/ contiene archivos que se sirven directamente sin procesamiento: imágenes, fuentes, favicon, robots.txt, etc.

Importante: Los archivos en public/ se copian tal cual al build final.

public/
favicon.svg
logo.png
robots.txt
images/
hero.jpg
banner.webp

Cómo referenciar archivos de public/:

1<!-- Imagen en public/logo.png -->
2<img src="/logo.png" alt="Logo" />
3
4<!-- Imagen en public/images/hero.jpg -->
5<img src="/images/hero.jpg" alt="Hero" />
6
7<!-- Favicon en public/favicon.svg -->
8<link rel="icon" type="image/svg+xml" href="/favicon.svg" />

Nota: Las rutas empiezan con / porque se sirven desde la raíz del sitio.

6. Buenas Prácticas

Usa layouts para estructura común

Crea layouts para headers, footers y estructura repetida. Evita duplicar código.

Componentes pequeños y reutilizables

Divide tu UI en componentes pequeños. Es más fácil mantener y reutilizar.

Organiza pages/ por funcionalidad

Usa subcarpetas en pages/ para agrupar rutas relacionadas: blog/, productos/, etc.

Imágenes optimizadas en public/

Usa formatos modernos (WebP, AVIF) y comprime imágenes antes de subirlas a public/.

Nombres descriptivos en inglés

Usa nombres claros para archivos y componentes: ProductCard.astro en vez de comp1.astro.

6.1. ¿Cómo funcionan las Props?

Las props (propiedades) son la forma de pasar datos a un componente o layout. Es como llamar a una función y pasarle parámetros.

Piensa en las props como variables que recibes desde fuera del componente.

1. Definir qué props acepta

En el componente/layout, usas Astro.props para recibir:

1---
2// Card.astro
3const { titulo, descripcion } = Astro.props;
4---
5
6<div class="card">
7 <h3>{titulo}</h3>
8 <p>{descripcion}</p>
9</div>

2. Pasar props al usarlo

Cuando usas el componente, le pasas valores:

1---
2import Card from '../components/Card.astro';
3---
4
5<Card
6 titulo="Mi Card"
7 descripcion="Esto es una descripción"
8/>

Flujo de las Props:

Página usa componente

<Card titulo="Hola" />

Componente recibe props

const { titulo } = Astro.props

Componente usa props

<h3>{titulo}</h3>

Analogía: Las props son como los ingredientes de una receta. Tú decides qué ingredientes (props) pasas, y el componente los usa para "cocinar" el HTML final.

6.2. ¿Cómo funcionan los Slots?

Los slots son "huecos" donde puedes insertar contenido HTML completo. Es como un espacio reservado que se rellena con lo que le pases.

Piensa en los slots como cajones vacíos que se llenan con el contenido que tú decidas.

Ejemplo 1: Slot básico (sin nombre)

PASO 1 Defines el componente con un "hueco" <slot />

📁 Archivo:

src/components/Card.astro
1---
2// Card.astro - Componente reutilizable
3---
4
5<div class="card">
6 <h3>Mi Card</h3>
7
8 <slot />
9 <!--
10 ↑ Este <slot /> es un HUECO
11 Lo que pongas entre <Card> y </Card>
12 aparecerá aquí
13 -->
14</div>
PASO 2 Lo que pongas entre <Card> y </Card> irá al slot

📁 Archivo:

src/pages/index.astro
1---
2import Card from '../components/Card.astro';
3---
4
5<Card>
6 <!-- ⬇️ TODO ESTO va al <slot /> del componente -->
7 <p>Este texto va al slot</p>
8 <button>Click aquí</button>
9 <!-- ⬆️ Esto reemplaza el <slot /> -->
10</Card>
11
12<!--
13 CLAVE: No es una etiqueta HTML normal.
14 <Card> es un COMPONENTE que tiene un <slot />
15 El contenido entre <Card> y </Card> se inserta en ese slot
16-->
RESULTADO El <slot /> fue reemplazado por el contenido:
1<div class="card">
2 <h3>Mi Card</h3>
3
4 <!-- El <slot /> fue reemplazado por: -->
5 <p>Este texto va al slot</p>
6 <button>Click aquí</button>
7</div>

Ejemplo 2: Slots con nombre (múltiples huecos)

PASO 1 El layout define 2 huecos con nombre diferente

📁 Archivo:

src/layouts/MainLayout.astro
1---
2// MainLayout.astro
3---
4
5<html>
6 <body>
7 <main>
8 <slot />
9 <!--
10 ↑ HUECO 1: Sin nombre
11 Aquí va el contenido "normal"
12 -->
13 </main>
14
15 <aside>
16 <slot name="sidebar" />
17 <!--
18 ↑ HUECO 2: Con nombre "sidebar"
19 Aquí va SOLO el contenido con slot="sidebar"
20 -->
21 </aside>
22 </body>
23</html>
PASO 2 Decides qué contenido va a cada slot usando slot="nombre"

📁 Archivo:

src/pages/blog.astro
1---
2import MainLayout from '../layouts/MainLayout.astro';
3---
4
5<MainLayout>
6 <!-- ⬇️ SIN slot="" = va al <slot /> sin nombre -->
7 <h1>Mi Blog</h1>
8 <p>Contenido principal</p>
9 <!-- ⬆️ Esto va al <main> -->
10
11 <!-- ⬇️ CON slot="sidebar" = va al <slot name="sidebar" /> -->
12 <div slot="sidebar">
13 <h3>Categorías</h3>
14 <ul>
15 <li>JavaScript</li>
16 <li>Astro</li>
17 </ul>
18 </div>
19 <!-- ⬆️ Esto va al <aside> -->
20</MainLayout>
21
22<!--
23 CLAVE:
24 - Sin slot="" → va al slot sin nombre
25 - Con slot="sidebar" → va al slot llamado "sidebar"
26-->
RESULTADO Cada slot fue reemplazado por su contenido:
1<html>
2 <body>
3 <main>
4 <!-- El <slot /> sin nombre fue reemplazado por: -->
5 <h1>Mi Blog</h1>
6 <p>Contenido principal</p>
7 </main>
8
9 <aside>
10 <!-- El <slot name="sidebar" /> fue reemplazado por: -->
11 <div>
12 <h3>Categorías</h3>
13 <ul>
14 <li>JavaScript</li>
15 <li>Astro</li>
16 </ul>
17 </div>
18 </aside>
19 </body>
20</html>

Ejemplo 3: Insertar componentes dentro del slot

¿Qué hace especial a los slots? Puedes insertar cualquier cosa: HTML básico, otros componentes, o una mezcla de ambos. Esto se llama composición de componentes.

PASO 1 Primero, creas un componente Button que recibe props

📁 Archivo:

src/components/Button.astro
1---
2// Button.astro - Componente de botón reutilizable
3const { texto, color = 'blue' } = Astro.props;
4---
5
6<button class={`btn btn-${color}`}>
7 {texto}
8</button>
PASO 2 Componente Card con un slot para contenido flexible

📁 Archivo:

src/components/Card.astro
1---
2// Card.astro
3const { titulo } = Astro.props;
4---
5
6<div class="card">
7 <h3>{titulo}</h3>
8
9 <slot />
10 <!--
11 ↑ Este slot puede recibir:
12 - HTML normal: <p>, <div>, etc.
13 - Otros componentes: <Button />, <Image />, etc.
14 - O una mezcla de ambos!
15 -->
16</div>
PASO 3 Usas Card con props + insertas Button (que también tiene props) en el slot

📁 Archivo:

src/pages/index.astro
1---
2import Card from '../components/Card.astro';
3import Button from '../components/Button.astro';
4---
5
6<Card titulo="Mi Tarjeta">
7 <!-- ⬇️ Dentro del slot: HTML + Componentes -->
8
9 <p>Este es el contenido de la tarjeta</p>
10
11 <!-- Insertas un componente Button con props -->
12 <Button texto="Click aquí" color="green" />
13 <Button texto="Cancelar" color="red" />
14
15 <!-- ⬆️ Todo esto va al <slot /> de Card -->
16</Card>
17
18<!--
19 CLAVE - Composición:
20 - Card recibe la prop "titulo"
21 - Dentro del slot de Card insertas:
22 * HTML normal (<p>)
23 * Componentes Button con sus propias props
24
25 Esto es MUY potente: combinas componentes reutilizables
26-->
RESULTADO HTML final con todo combinado:
1<div class="card">
2 <h3>Mi Tarjeta</h3>
3
4 <!-- El slot fue reemplazado por: -->
5 <p>Este es el contenido de la tarjeta</p>
6
7 <!-- Los componentes Button se renderizaron: -->
8 <button class="btn btn-green">
9 Click aquí
10 </button>
11 <button class="btn btn-red">
12 Cancelar
13 </button>
14</div>
15
16<!--
17 ✨ Resultado:
18 1. Card usó su prop "titulo" → <h3>Mi Tarjeta</h3>
19 2. El <slot /> se reemplazó por todo el contenido
20 3. Los componentes Button usaron sus props (texto y color)
21-->

🎯 Ventaja de combinar Props + Slots

✓ Props:

Para datos simples y configuración

titulo="Mi Tarjeta"

✓ Slots:

Para contenido complejo y flexible

<Card>contenido complejo</Card>

Composición = Puedes anidar componentes dentro de otros. Card no necesita saber qué contiene (puede ser Button, Image, Video, etc.). Los slots lo hacen flexible y reutilizable.

Regla para recordar:

Slot sin nombre:

<slot />

El contenido que NO tiene atributo slot="" va aquí

✓ Esto va al slot sin nombre:
<Card>contenido</Card>

Slot con nombre:

<slot name="sidebar" />

Solo va el contenido que tenga slot="sidebar"

✓ Esto va al slot "sidebar":
<div slot="sidebar">...</div>

Analogía del slot: Cuando usas <Card>contenido</Card>, NO es una etiqueta HTML como <div>. Es un componente que tiene un hueco (<slot />) donde se inserta tu contenido.

Props vs Slots - ¿Cuándo usar cada uno?

Usa Props para:

  • Datos simples: texto, números, booleanos
  • Configuración: titulo, color, tamaño
  • Ejemplo: titulo="Hola"

Usa Slots para:

  • HTML complejo: párrafos, listas, imágenes
  • Contenido variable: el contenido principal de una página
  • Ejemplo: <slot />

Regla simple: Si es un texto corto → Props. Si es HTML completo → Slot.

7. Ejercicios Prácticos

Pon a prueba lo que has aprendido con estos ejercicios. Intenta resolverlos por tu cuenta antes de ver la solución.

EJERCICIO 1

Layout con Navegación y Páginas

Enunciado:

Crea un layout llamado MainLayout.astro en src/layouts/ que tenga:

  • Props: titulo (para el <title>) y descripcion (para meta description)
  • Un <header> con "Mi Sitio Web" y navegación con enlaces: Inicio (/), Blog (/blog), Contacto (/contacto)
  • Enlaces resaltados cuando estés en esa página (usa Astro.url.pathname)
  • Layout de 2 columnas: <main> con slot principal + <aside> con slot sidebar
  • Un <footer> con "© 2025 - Mi Sitio"

Luego, crea 3 páginas en src/pages/:

  • 1. index.astro - Página de inicio con contenido principal y sidebar
  • 2. blog.astro - Página de blog con lista de artículos y sidebar
  • 3. contacto.astro - Página de contacto con formulario y sidebar

Objetivo: Todas las páginas deben usar el mismo layout. Al navegar, el enlace activo debe cambiar. Los estilos CSS son opcionales - céntrate en entender Astro.

Ver Solución

src/layouts/MainLayout.astro

1---
2const { titulo, descripcion } = Astro.props;
3const rutaActual = Astro.url.pathname;
4---
5
6<!DOCTYPE html>
7<html lang="es">
8<head>
9 <meta charset="UTF-8">
10 <meta name="viewport" content="width=device-width, initial-scale=1.0">
11 <meta name="description" content={descripcion}>
12 <title>{titulo}</title>
13</head>
14<body>
15 <header>
16 <h1>Mi Sitio Web</h1>
17 <nav>
18 <a href="/" class={rutaActual === '/' ? 'active' : ''}>Inicio</a>
19 <a href="/blog" class={rutaActual === '/blog' ? 'active' : ''}>Blog</a>
20 <a href="/contacto" class={rutaActual === '/contacto' ? 'active' : ''}>Contacto</a>
21 </nav>
22 </header>
23
24 <div class="container">
25 <main>
26 <slot />
27 </main>
28
29 <aside>
30 <slot name="sidebar" />
31 </aside>
32 </div>
33
34 <footer>
35 <p>© 2025 - Mi Sitio</p>
36 </footer>
37</body>
38</html>
39
40<style>
41 /* Estilos mínimos - personalízalos como quieras */
42 .container {
43 display: grid;
44 grid-template-columns: 1fr 300px;
45 gap: 2rem;
46 }
47
48 nav a.active {
49 font-weight: bold;
50 }
51</style>

src/pages/index.astro

1---
2import MainLayout from '../layouts/MainLayout.astro';
3---
4
5<MainLayout
6 titulo="Inicio - Mi Sitio Web"
7 descripcion="Bienvenido a mi sitio web personal"
8>
9 <h2>Bienvenido a mi sitio</h2>
10 <p>Esta es la página de inicio con contenido principal.</p>
11 <p>Navega por las diferentes secciones usando el menú superior.</p>
12
13 <div slot="sidebar">
14 <h3>Sobre mí</h3>
15 <p>Desarrollador web aprendiendo Astro.</p>
16 <h4>Enlaces rápidos</h4>
17 <ul>
18 <li><a href="/blog">Ver Blog</a></li>
19 <li><a href="/contacto">Contactar</a></li>
20 </ul>
21 </div>
22</MainLayout>

src/pages/blog.astro

1---
2import MainLayout from '../layouts/MainLayout.astro';
3
4const articulos = [
5 { id: 1, titulo: 'Mi primer artículo', fecha: '2025-01-15' },
6 { id: 2, titulo: 'Aprendiendo Astro', fecha: '2025-01-20' },
7 { id: 3, titulo: 'Layouts y componentes', fecha: '2025-01-25' }
8];
9---
10
11<MainLayout
12 titulo="Blog - Mi Sitio Web"
13 descripcion="Lee mis últimos artículos sobre desarrollo web"
14>
15 <h2>Blog</h2>
16 <p>Bienvenido a mi blog. Aquí publico artículos sobre desarrollo web.</p>
17
18 {articulos.map(articulo => (
19 <article>
20 <h3>{articulo.titulo}</h3>
21 <p><small>{articulo.fecha}</small></p>
22 <p>Resumen del artículo...</p>
23 </article>
24 ))}
25
26 <div slot="sidebar">
27 <h3>Categorías</h3>
28 <ul>
29 <li>JavaScript</li>
30 <li>Astro</li>
31 <li>CSS</li>
32 </ul>
33 <h3>Archivos</h3>
34 <ul>
35 <li>Enero 2025</li>
36 <li>Diciembre 2024</li>
37 </ul>
38 </div>
39</MainLayout>

src/pages/contacto.astro

1---
2import MainLayout from '../layouts/MainLayout.astro';
3---
4
5<MainLayout
6 titulo="Contacto - Mi Sitio Web"
7 descripcion="Ponte en contacto conmigo"
8>
9 <h2>Contacto</h2>
10 <p>¿Tienes alguna pregunta? Envíame un mensaje.</p>
11
12 <form>
13 <div>
14 <label for="nombre">Nombre:</label>
15 <input type="text" id="nombre" name="nombre" required>
16 </div>
17
18 <div>
19 <label for="email">Email:</label>
20 <input type="email" id="email" name="email" required>
21 </div>
22
23 <div>
24 <label for="mensaje">Mensaje:</label>
25 <textarea id="mensaje" name="mensaje" rows="5" required></textarea>
26 </div>
27
28 <button type="submit">Enviar</button>
29 </form>
30
31 <div slot="sidebar">
32 <h3>Info de contacto</h3>
33 <ul>
34 <li><strong>Email:</strong> info@example.com</li>
35 <li><strong>Twitter:</strong> @miusuario</li>
36 <li><strong>GitHub:</strong> github.com/usuario</li>
37 </ul>
38 </div>
39</MainLayout>

Conceptos practicados: Props múltiples, Astro.url.pathname, slots con nombre, routing basado en archivos, reutilización de layouts, clases condicionales.

EJERCICIO 2

Crear un Componente Reutilizable

Enunciado:

Crea un componente llamado Button.astro en la carpeta src/components/ que:

  • Acepte dos props: text (el texto del botón) y url (la URL a la que redirige)
  • Renderice un enlace <a> con estilos de botón
  • Tenga un estilo CSS básico (color de fondo, padding, border-radius)

Luego, usa el componente 3 veces en una página para crear 3 botones diferentes.

Ver Solución

src/components/Button.astro

1---
2const { text, url } = Astro.props;
3---
4
5<a href={url} class="button">{text}</a>
6
7<style>
8 .button {
9 display: inline-block;
10 padding: 0.75rem 1.5rem;
11 background-color: #3b82f6;
12 color: white;
13 text-decoration: none;
14 border-radius: 0.5rem;
15 font-weight: 600;
16 transition: background-color 0.3s;
17 }
18
19 .button:hover {
20 background-color: #2563eb;
21 }
22</style>

src/pages/ejemplo.astro (usando el componente)

1---
2import Button from '../components/Button.astro';
3---
4
5<!DOCTYPE html>
6<html lang="es">
7<head>
8 <meta charset="UTF-8">
9 <title>Ejemplo de Botones</title>
10</head>
11<body>
12 <h1>Mis Botones</h1>
13
14 <Button text="Ir al inicio" url="/" />
15 <Button text="Ver Blog" url="/blog" />
16 <Button text="Contactar" url="/contacto" />
17</body>
18</html>

Resumen

📄 src/pages/ - Cada archivo = una ruta (routing basado en archivos)
🎨 src/layouts/ - Plantillas reutilizables para estructura común
🧩 src/components/ - Componentes UI reutilizables
📁 public/ - Archivos estáticos (imágenes, fuentes, favicon)
Usa layouts para evitar repetir código de estructura
Crea componentes pequeños y reutilizables