🚀 Astro 7.4 — Build Timings, Image Services y Prerender Paralelo

La release que hace visible el build

Astro 7.4 salió el 15 de octubre de 2026 (autor: Matthew Phillips). La estrella es --timings, un flag de astro build que muestra exactamente dónde se va el tiempo. Además: servicios de imagen distintos para páginas prerenderizadas y on-demand, notFound() para entrypoints personalizados, avisos de configuración de fuentes, Node.js mínimo 22.22.0 y dos features experimentales.

  • 🌐 Anuncio: astro.build/blog/astro-740
  • 📦 Upgrade: npx @astrojs/upgrade
  • 🧪 Experimentales: parallelPrerender · redirectPage

⬆️ Actualizar

# Recomendado
npx @astrojs/upgrade
 
# Manual
npm install astro@latest
# o
pnpm upgrade astro --latest
# o
yarn upgrade astro --latest

Node.js primero

El mínimo soportado para Astro y sus integraciones oficiales ahora es Node.js 22.22.0. Si estás en una 22 anterior, actualiza Node antes que Astro.


⏱️ Build timings (--timings)

Cuando un build se vuelve lento, no siempre es obvio qué lo causa: ¿un plugin de Vite, syntax highlighting, procesamiento de imágenes o una página pesada?

astro build --timings

Al terminar, Astro imprime un resumen de:

  • Fases del build
  • Plugins de Vite
  • Procesamiento de Markdown y syntax highlighting
  • Renderizado de páginas
  • Imágenes
  • Integraciones

Ejemplo real del anuncio

Un blog starter con un componente <CommentSection> que hace fetch de comentarios y luego, por cada comentario, fetch del perfil del autor:

total 2.61s · 9 pages · output "static"
 
generate pages      2.10s  80.6%
  component render  2.08s  79.6%
  page pipeline       13ms   0.5%
 
Slowest pages (median 297ms · p95 571ms)
  /about              571ms
  /blog/first-post    307ms
  /blog/using-mdx     303ms
  ...

Diagnóstico: el 81% del build se va en generar páginas, y casi todo dentro de componentes, no en el pipeline de Astro. Las páginas lentas son las que usan el layout del post.

Fix: hacer los fetches de comentarios y perfiles en paralelo con Promise.all() y pasar cada perfil a <Comment>. Resultado: de 2.10s a 1.13s en generación, y la mitad de tiempo por post.

Úsalo antes de optimizar a ciegas

--timings es el primer paso antes de probar parallelPrerender o builds incrementales: te dice qué parte atacar. También cubre plugins de Vite, archivos lentos de bundlear, plugins de Markdown, highlighting e imágenes.


🖼️ Servicios de imagen separados (build vs runtime)

Las páginas prerenderizadas y las on-demand tienen necesidades distintas:

MomentoEntornoServicio ideal
BuildNode.js completo, sin prisaOptimizador local como Sharp
RuntimeServer/edge, a veces sin SharpImage CDN

Antes había que elegir uno para ambos o dejar que el adapter decidiera. Ahora image.service acepta build y runtime:

// astro.config.mjs
import { defineConfig } from 'astro/config';
 
export default defineConfig({
  image: {
    service: {
      build: { entrypoint: 'astro/assets/services/sharp' },
      runtime: { entrypoint: 'my-image-cdn-service' },
    },
  },
});
  • build — imágenes de páginas prerenderizadas, generadas durante el build. También se usa en desarrollo.
  • runtime — páginas on-demand y el endpoint de imágenes en producción.
  • Un solo servicio sigue funcionando igual que antes.

Para autores de integraciones

  • config.image.service conserva la forma configurada.
  • Si el proyecto define build y runtime, config.image.service.entrypoint y .config son undefined. Hay que comprobar la propiedad runtime:
'astro:config:done': ({ config }) => {
  const { service } = config.image;
  const runtimeService = 'runtime' in service ? service.runtime : service;
  // ...
},
  • Los prerenderers personalizados deben reportar las imágenes de cada página en la metadata que devuelven getStaticPaths() y render().
  • Devolver un array plano desde getStaticPaths() o un Response plano desde render() sigue funcionando pero está deprecado, y las imágenes de esas páginas no se generan.
  • Actualizar @astrojs/cloudflare y @astrojs/markdoc junto con astro, porque sus nuevas versiones requieren astro@^7.4.0.

Cambio de pipeline

Astro reworked cómo rastrea las imágenes del build: los prerenderers ahora participan del pipeline propio de Astro. @astrojs/cloudflare procesa imágenes de páginas prerenderizadas con el mismo pipeline que el resto de adapters.


🧭 notFound() para entrypoints personalizados

Los handlers composables de astro/fetch y astro/hono permiten armar tu propio server/worker. Si usabas pages() sin el handler todo-en-uno astro(), una ruta inexistente devolvía un 404 vacío en vez de tu 404.astro, incluso si estaba prerenderizada.

Astro 7.4 agrega notFound(). Debe ir antes de pages():

// src/worker.ts
import { Hono } from 'hono';
import { notFound, pages } from 'astro/hono';
import { cf } from '@astrojs/cloudflare/hono';
 
const app = new Hono();
app.use(cf());
app.use(notFound());
app.use(pages());
 
export default app;

El mismo handler existe en astro/fetch. astro() ya incluye este comportamiento, así que solo necesitas notFound() si compones los handlers a mano.


🔤 Avisos de configuración de fuentes

Es fácil pedir un peso o subset que la familia no ofrece, y antes solo lo descubrías cuando el texto se veía mal.

Los font providers ahora pueden implementar getFontProperties(), un callback opcional que reporta:

  • Pesos (weights)
  • Estilos (styles)
  • Subsets
  • Formatos

Astro avisa si tu configuración pide algo que el provider no puede servir. Todos los providers integrados lo soportan.

import type { FontProvider } from 'astro';
 
const provider: FontProvider = {
  name: 'my-provider',
  resolveFont: ({ familyName }) => {
    // ...
  },
  getFontProperties: ({ familyName }) => ({
    weights: ['400', '700'],
    styles: ['normal', 'italic'],
    subsets: ['latin'],
    formats: ['woff2'],
  }),
};

Obligatorio en Astro 8

getFontProperties() es opcional en Astro 7, pero será requerido en Astro 8. Si mantienes un provider custom, conviene implementarlo ya.

El contexto de init() también gana un fetch() que reintenta fallos transitorios; los providers que envuelven un provider de unifont pueden pasarlo tal cual.


⚙️ Experimental: prerender paralelo

Astro renderiza páginas estáticas concurrentemente, pero hasta ahora todo ocurría en un solo hilo. En sitios grandes con páginas costosas de CPU, el build quedaba limitado a un core.

// astro.config.mjs
export default defineConfig({
  experimental: {
    parallelPrerender: true,
  },
});
  • Por defecto usa todos los cores disponibles menos uno.
  • Cada worker aumenta la memoria del build. Si hay OOM o no mejora, bajar el número:
experimental: {
  parallelPrerender: {
    workers: 4,
  },
},
  • Cada worker renderiza tantas páginas a la vez como permita build.concurrency.
  • No tiene efecto cuando el adapter provee su propio prerendering. Entre los oficiales, solo @astrojs/cloudflare; Astro avisa durante el build cuando se ignora.
  • Se combina bien con incremental static builds: los incrementales saltan páginas sin cambios y el paralelo reparte las que sí hay que renderizar.

🔀 Experimental: página de redirect personalizada

Un sitio estático no tiene servidor para mandar un header Location, así que Astro genera una página HTML con <meta http-equiv="refresh"> por cada redirect: config redirects, Astro.redirect() en páginas prerenderizadas y redirects de i18n.

Antes el markup, texto y delay eran fijos. Ahora:

experimental: {
  redirectPage: true,
},

Crear src/pages/3xx.astro. Astro lo renderiza una vez por redirect y pasa props:

PropContenido
fromRuta origen
toRuta destino
statusCódigo de estado
delaySegundos de espera
---
import type { RedirectPageProps as Props } from 'astro';
const { from, to, status, delay } = Astro.props;
---
<html lang="en">
  <head>
    <meta http-equiv="refresh" content={`${delay};url=${to}`} />
    <meta name="robots" content="noindex" />
    <title>Redirecting…</title>
  </head>
  <body>
    <p>This page has moved. <a href={to}>Continue to {to}</a>.</p>
  </body>
</html>

El meta refresh es el redirect

En build estático tu página debe renderizar un <meta http-equiv="refresh"> que apunte a to. Astro avisa si falta y cae al page built-in si el tuyo falla. Los redirects de páginas on-demand no cambian.


🧩 Otras mejoras

  • Integraciones controlan el output: Astro trata el outDir resuelto por Vite para cada entorno como fuente de verdad de dónde se escribe client, server y prerender. Las integraciones y plugins de Vite pueden moverlo.
  • outputDirectories: nuevo contexto en setPrerenderer() para que los prerenderers custom lean las ubicaciones finales.
  • Build manifests: los manifiestos de Vite se conservan hasta que terminan todos los hooks buildApp, para que las integraciones de deploy puedan leerlos (#18313).

⚠️ Notas prácticas

  • Node 22.22.0 es el nuevo piso; revisar CI y contenedores antes de actualizar.
  • --timings no optimiza solo: identifica el cuello; el fix sigue siendo tuyo.
  • Imagen split: si mantienes una integración que lee config.image.service.entrypoint, añade el check de runtime.
  • Adapters: actualizar @astrojs/cloudflare y @astrojs/markdoc a la par de astro@^7.4.0.
  • Experimentales: parallelPrerender y redirectPage pueden cambiar; no los uses en producción sin probar.
  • Memoria: el prerender paralelo multiplica el consumo; medir con --timings y ajustar workers.

🔗 Enlaces relacionados