La estructura de directorios
La anatomía de un proyecto Astro: src/pages y public son las dos carpetas con significado especial; src/components, src/layouts, src/content y src/styles son convención. Qué va exactamente en cada sitio y por qué.
Un proyecto de Astro parece tener muchas carpetas con nombres cargados de intención, pero solo dos tienen significado para el framework: src/pages y public. Todo lo demás es convención —útil, compartida, recomendable, pero convención—. Distinguir lo mágico de lo acordado es el primer paso para dominar la estructura en lugar de obedecerla a ciegas.
- Distinguir las dos carpetas con significado especial de las que son mera convención.
- Ubicar componentes, layouts y estilos en
srcy saber qué va en cada sitio. - Entender el papel de
src/contenty de la configuración de colecciones. - Diferenciar los assets procesados por Vite de los servidos tal cual desde
public.
El árbol de un proyecto
Tras el andamiaje, un proyecto mínimo tiene esta forma. En la raíz viven los ficheros de configuración; dentro de src vive tu código; public guarda lo estático sin procesar.
flowchart TD R[raiz del proyecto] --> S[src] R --> P[public] R --> C[astro.config.mjs] R --> M[package.json y tsconfig.json] S --> PG[pages] S --> CM[components] S --> LY[layouts] S --> CT[content] S --> ST[styles] PG --> RT[genera las rutas del sitio] P --> AS[se sirve tal cual en la raiz] style PG fill:#89b4fa,color:#11111b style P fill:#89b4fa,color:#11111b style S fill:#a6e3a1,color:#11111b
En azul, las dos carpetas que Astro trata de forma especial; en verde, src, el contenedor de todo lo que Vite procesa. La distinción no es estética: define qué ocurre con cada fichero cuando construyes el sitio.
En la raíz, fuera de src, viven los ficheros que gobiernan el proyecto entero: astro.config.mjs con la configuración, package.json con dependencias y scripts, y tsconfig.json con los ajustes de TypeScript. No contienen páginas ni componentes; describen cómo se comporta todo lo demás. Conviene tenerlos localizados desde el principio, porque a ellos volverás cada vez que añadas una integración o cambies el modo de renderizado.
src/pages: la única carpeta que enruta
src/pages es el corazón del framework. Astro aplica aquí enrutado por ficheros: cada archivo se convierte en una ruta según su ruta relativa. No hay que declarar rutas en ningún sitio; la estructura del sistema de ficheros es el mapa de URLs.
src/pages/index.astrogenera la raíz, la URL/.src/pages/blog/index.astrogenera/blog.src/pages/blog/primer-post.astrogenera/blog/primer-post.
Admite varios formatos como páginas: .astro, .md, .mdx y .html producen páginas; los ficheros .js y .ts producen endpoints —respuestas de API en vez de HTML—. Fuera de src/pages, ninguno de esos ficheros crea una ruta.
El enrutado también entiende parámetros. Un nombre de fichero entre corchetes captura un segmento dinámico, y uno con puntos suspensivos captura varios de golpe:
src/pages/blog/[slug].astroresponde a/blog/lo-que-sea.src/pages/docs/[...ruta].astroresponde a/docs/con/varios/niveles.
Esta misma guía se sirve así: una sola plantilla dinámica genera cientos de lecciones leyendo las colecciones de contenido. Verás el enrutado dinámico a fondo más adelante; por ahora basta con saber que los corchetes en src/pages son la puerta a rutas generadas por datos.
La contrapartida de esa comodidad es una disciplina: no dejes ficheros sueltos en src/pages que no sean páginas. Cualquier .astro que aterrice ahí se convierte en una URL pública, casi siempre una que no querías exponer. Los componentes auxiliares viven en src/components, nunca en src/pages.
Este es el malentendido más común al empezar. Un .astro precioso en src/components no tiene URL: no se puede visitar. Solo lo que vive bajo src/pages genera rutas navegables. Los componentes, layouts y utilidades existen para ser importados por las páginas, no para servirse por sí mismos.
src/components, layouts y styles: convención útil
El resto de carpetas dentro de src no significan nada especial para Astro: son nombres que la comunidad acordó para ordenar el código. Podrías renombrarlas y todo seguiría funcionando siempre que ajustaras tus imports. Aun así, respétalas: la convención compartida es lo que hace que cualquiera entienda tu proyecto de un vistazo.
src/components
Piezas reutilizables de UI —un botón, una tarjeta, un buscador—. Se importan desde páginas y layouts. Aquí conviven .astro y componentes de framework.
src/layouts
Plantillas de página que envuelven contenido con un <slot />: cabecera, pie y estructura común. Un layout es un componente con vocación de marco.
src/styles
CSS global y variables de diseño. Lo importas desde un layout o una página; Vite lo procesa, agrupa y versiona con un hash.
src/lib o src/utils
Lógica que no es UI: helpers, clientes de datos, tipos. Puro TypeScript que tus componentes consumen. El nombre es libre.
Un layout es solo un componente que coloca un <slot /> donde irá el contenido de cada página que lo use:
---
// src/layouts/Base.astro
const { titulo } = Astro.props;
---
<html lang="es">
<head><title>{titulo}</title></head>
<body>
<slot />
</body>
</html>
Ese <slot /> es el hueco; una página envuelve su contenido con el layout y aparece ahí. Es toda la mecánica que separa src/layouts de src/components: intención, no magia del framework.
Como estas carpetas no son mágicas, eres libre de anidarlas y co-localizar: agrupar un componente con su estilo y su test en una subcarpeta, o crear src/features con módulos completos. Astro no impone jerarquía; solo te pide que las páginas encuentren lo que importan.
La clave mental es que todo lo que vive en src pasa por el pipeline de Vite: se analiza, se agrupa, se optimiza y se le añade un hash al nombre para cachearlo con seguridad. Por eso una imagen importada desde src/assets puede optimizarse y transformarse, mientras que una imagen en public sale intacta.
src/content y public: contenido y estáticos
src/content es la carpeta convencional para tus colecciones de contenido: el Markdown y el MDX de tu blog, tus docs o tu catálogo. Las colecciones se declaran en src/content.config.ts, donde defines un esquema tipado con Zod; a partir de ahí tu frontmatter tiene tipos y autocompletado. Verás las colecciones a fondo más adelante; por ahora, quédate con que esta carpeta es el hogar natural del contenido estructurado. Desde la Content Layer, el contenido incluso puede vivir fuera de src/content o venir de una fuente remota; la carpeta es una convención cómoda, no una obligación del framework.
mi-proyecto/
├─ src/
│ ├─ pages/ rutas del sitio (especial)
│ ├─ components/ UI reutilizable (convencion)
│ ├─ layouts/ plantillas de pagina (convencion)
│ ├─ content/ colecciones md y mdx (convencion)
│ └─ styles/ css global (convencion)
├─ public/ servido tal cual (especial)
├─ astro.config.mjs configuracion del proyecto
├─ tsconfig.json configuracion de typescript
└─ package.json dependencias y scripts
Ese árbol condensa la regla entera: junto a cada carpeta, si es especial o convención. Interiorizar esa columna te ahorra consultar la documentación cada vez que dudes dónde poner algo.
public, en cambio, es la otra carpeta especial. Todo lo que pongas dentro se copia sin tocar a la raíz del sitio publicado: public/favicon.svg acaba en /favicon.svg, public/robots.txt en /robots.txt. Ni bundling, ni hash, ni optimización. Es el sitio para ficheros que deben conservar su nombre y ruta exactos: el favicon, el robots.txt, el manifest, fuentes que sirves crudas o verificaciones de dominio.
Con el tiempo verás aparecer tres carpetas más que no creaste tú y que no debes tocar: node_modules con las dependencias, dist con el resultado del build, y .astro con los tipos generados. Las tres son artefactos regenerables y van al .gitignore. Reconocerlas evita el susto de creer que forman parte de tu código fuente.
Regla práctica: si una imagen forma parte del contenido y quieres que Astro la optimice —redimensionar, convertir a formatos modernos, versionar—, ponla en src e impórtala. Si es un fichero que debe existir con un nombre y ruta fijos, o que referencias por URL absoluta, va en public. La diferencia es procesamiento contra literalidad.
Con estas piezas ya tienes el mapa completo: sabes qué genera rutas, qué se copia intacto, qué procesa Vite y qué es solo convención. Es suficiente para moverte por cualquier proyecto Astro sin perderte y para colocar cada fichero nuevo en su sitio a la primera.
La estructura de un proyecto Astro enseña una lección de diseño que trasciende al framework: minimiza la magia y hazla visible. De todas las carpetas que ves, únicamente dos alteran el comportamiento del sistema —src/pages, que convierte ficheros en rutas, y public, que copia ficheros sin tocarlos—. El resto son acuerdos humanos: components, layouts, styles, content podrían llamarse de cualquier forma y Astro no se inmutaría, porque a él solo le importa lo que las páginas importan, no dónde vive lo importado. Esta economía de convenciones especiales es deliberada. Un framework que dota de significado mágico a diez carpetas te obliga a memorizar diez reglas y te castiga cuando te desvías; uno que solo reserva dos te deja organizar el resto como tu proyecto pida. El sistema de ficheros se vuelve así una API legible: miras el árbol y sabes qué es ruta, qué es estático y qué es tuyo para ordenar. Cuando interiorices que solo pages y public son especiales, dejarás de tratar la estructura como un ritual y empezarás a usarla como una herramienta que se dobla a tu voluntad.
- En tu proyecto recién creado, localiza
src/pagesypublic; abresrc/pages/index.astroy relaciónalo con la URL/. - Crea
src/pages/hola.astrocon un<h1>y visita/hola: comprueba que el enrutado por ficheros funciona sin declarar nada. - Añade un
public/robots.txty confirma que se sirve intacto en/robots.txt, sin hash ni transformación. - Renombra mentalmente
src/componentsasrc/piezas: enumera qué imports tendrías que ajustar y verifica que Astro no exigiría nada más.