import.meta.glob: datos y assets fuera de las colecciones
Importar muchos ficheros por patrón sin declararlos uno a uno: usar import.meta.glob para traer JSON de datos e imágenes que no viven en content collections, elegir entre la carga eager y la lazy, seleccionar una exportación concreta con import, y obtener imágenes procesadas de astro:assets desde una galería globada.
import.meta.glob: importar por patrón
import.meta.glob es una función de Vite, no exclusiva de Astro, disponible en cualquier módulo del proyecto. Le pasas un patrón glob relativo al fichero actual y te devuelve un objeto cuyas claves son las rutas encontradas y cuyos valores son cargadores. El patrón tiene que ser una cadena literal: Vite lo analiza en el build para enumerar las coincidencias, así que no puede salir de una variable calculada en tiempo de ejecución.
const modulos = import.meta.glob('./datos/*.json');// {// './datos/a.json': () => import('./datos/a.json'),// './datos/b.json': () => import('./datos/b.json'),// }Por defecto la carga es diferida: cada valor es una función que devuelve una promesa, y nada se importa hasta que la llamas. Ese diseño habilita el troceado de código —cada fichero puede acabar en su propio bloque, cargado solo cuando se necesita— y es la razón de que el valor no sea el módulo directamente, sino una puerta hacia él.
Las claves del objeto son las rutas tal como casaron, en orden alfabético estable, lo que te da un recorrido predecible sin ordenar nada. Y como todo se resuelve en el build, el patrón admite comodines de carpeta —./datos/**/*.json recorre subdirectorios— y varias extensiones a la vez, como en *.{jpg,png,webp}. Lo que no admite es incertidumbre en tiempo de ejecución: el conjunto de ficheros queda fijado al compilar.
eager frente a lazy
La opción eager invierte ese comportamiento. Con { eager: true }, Vite importa todos los ficheros en el build y los valores del objeto son ya los módulos, accesibles de forma síncrona sin await. Conviene para conjuntos pequeños que siempre necesitas —un puñado de JSON de configuración—. La carga diferida, en cambio, brilla con conjuntos grandes o condicionales, donde importar todo por adelantado hincharía el resultado y prefieres pagar el coste solo por lo que de verdad se use.
const datos = import.meta.glob('./datos/*.json', { eager: true });for (const ruta in datos) { console.log(ruta, datos[ruta].default);}La decisión tiene consecuencias medibles cuando el glob se usa en una isla que viaja al cliente: eager mete todos los módulos en el bundle, mientras que la carga diferida deja que el empaquetador los parta y los sirva solo al pedirlos. En el servidor la diferencia es de tiempo de build; en el cliente, de kilobytes que el usuario descarga. Elige eager para lo que siempre necesitas y diferido para lo que solo a veces.
Elegir la exportación: import y query
Sin más opciones, cada valor es el espacio de nombres completo del módulo, y accedes a su exportación por defecto con .default. La opción import te ahorra ese paso: { import: 'default' } hace que el valor sea directamente la exportación por defecto, e { import: 'nombre' } una exportación nombrada. Combinada con eager, te da un mapa limpio de ruta a dato. Y { query: '?url', import: 'default' } devuelve la URL del fichero en vez de su contenido, útil para assets que solo quieres enlazar.
const jsons = import.meta.glob('./datos/*.json', { eager: true, import: 'default',});// { './datos/a.json': { ...contenido }, './datos/b.json': { ...contenido } }Un patrón común es transformar ese objeto de ruta a módulo en un mapa indexado por algo útil —el nombre del fichero sin extensión, o un identificador de dentro del propio JSON— para consultarlo luego por clave. Así conviertes una carpeta en una tabla de búsqueda: pides datos[clave] en vez de recorrerlo todo. Es la misma idea de las colecciones un escalón más abajo, sin esquema ni validación, pero con la misma comodidad de que añadir un fichero baste para añadir una fila.
const modulos = import.meta.glob('./datos/*.json', { eager: true, import: 'default',});const porNombre = Object.fromEntries( Object.entries(modulos).map(([ruta, dato]) => [ ruta.split('/').pop()?.replace('.json', ''), dato, ]),);// porNombre.madrid -> el contenido de madrid.jsonImágenes y astro:assets
El caso más vistoso es una galería. Globas una carpeta de imágenes y, como Astro procesa los assets importados, cada valor es un objeto ImageMetadata con src, ancho, alto y formato, listo para el componente <Image>. Con eager e import por defecto obtienes un mapa de metadatos que recorres para pintar la galería entera.
---import { Image } from 'astro:assets';const imagenes = import.meta.glob<{ default: ImageMetadata }>( './galeria/*.{jpg,png,webp}', { eager: true },);---{Object.values(imagenes).map((mod) => ( <Image src={mod.default} alt="" />))}Esto resuelve un problema real: un src de imagen construido desde una variable en tiempo de ejecución no se procesa, porque Astro debe conocer el asset en el build para optimizarlo. Globar la carpeta le da esa lista por adelantado, de modo que cada imagen entra en la canalización de optimización —formatos modernos, tamaños responsivos— aunque la pintes en un bucle.
El mismo mecanismo alimenta otros usos: un mapa de componentes por nombre para incrustar en MDX, un conjunto de fragmentos de texto, los datos de varios idiomas en carpetas paralelas. Siempre que tengas una carpeta homogénea y quieras tratarla como un todo, import.meta.glob te ahorra la lista de importaciones a mano y, con ella, el olvido inevitable de actualizarla cuando el conjunto cambia.