Cómo está hecha esta web: estática donde se puede, en streaming donde hace falta
Esta web también es un proyecto de referencia. Tiene tres idiomas, decenas de páginas, más de una docena de herramientas interactivas y una guía de IA que responde mientras escribe. Saca 100 en las cuatro categorías de Lighthouse en móvil y funciona con los niveles gratuitos de Azure Static Web Apps y de la API de Gemini. Nada de eso necesita un stack exótico: React, Vite, un paso de prerender y un puñado de comprobaciones que se niegan a desplegar algo roto. Así encajan las piezas, incluidas dos cosas que solo descubrí midiendo.
10 min de lectura
1. Una fuente de verdad, muchas salidas
Los artículos, las páginas de servicios, los proyectos y las herramientas viven como datos tipados en el repositorio, un archivo por artículo y por servicio. La compilación convierte esos datos en todo lo que necesita un lector o una máquina: las páginas, un índice para los listados, un sitemap con las tres versiones de idioma de cada URL, un feed RSS por idioma, una imagen para compartir por artículo e idioma, el resumen llms.txt para asistentes de IA y el archivo de conocimiento con el que responde la guía.
Los dos últimos se mantenían a mano y se desincronizaron la primera vez que cambió el nombre de un producto. Ahora un script los regenera con los mismos datos en cada compilación, y una comprobación falla si la versión guardada no coincide. Nada se escribe dos veces, así que nada puede contradecirse.
// package.json
"build": "node scripts/gen-posts-meta.mjs && node scripts/gen-knowledge.mjs
&& tsc --noEmit -p tsconfig.app.json && vite build
&& node scripts/sitemap.mjs && node scripts/prerender.mjs"2. Prerenderizar cada página, en cada idioma
La web es una app de React, pero nadie descarga una carcasa vacía. Tras la compilación con Vite, un script arranca la web compilada en local, abre cada ruta en cada idioma en un Chrome sin interfaz y guarda el HTML terminado en su propio archivo: cada página por tres idiomas. Cada uno tiene su título, su descripción, su URL canónica, enlaces hreflang a los otros dos idiomas y datos estructurados para artículos, FAQ y migas de pan. La misma pasada genera una imagen para compartir por artículo.
Los idiomas van en la ruta: inglés en la raíz, alemán en /de, español en /es. Azure Static Web Apps sirve cada archivo prerenderizado directamente mediante reglas de reescritura que genera la compilación, y una salvaguarda hace fallar la compilación si dos reglas chocan, cosa que pasó una vez con /de y /de/. Para los rastreadores es HTML estático normal; para las personas, la app de React toma el control y cada clic posterior es instantáneo.
3. No desperdiciar el primer pintado
Tres mediciones cambiaron el código. Primero, los visitantes en alemán y español de la portada se redirigían a /de o /es solo después de cargar el bundle principal de 150 KB, lo que costaba un segundo en móvil. Ahora la redirección va como un script en línea de diez líneas al principio del HTML. La Content Security Policy permite exactamente ese script por su hash SHA-256, y la compilación falla si script y hash se separan.
Segundo, React sustituía la página prerenderizada por un marcador vacío hasta que llegaba el fragmento de código de la página, así que el contenido parpadeaba y Lighthouse contaba el segundo pintado. Ahora el navegador carga ese fragmento, y en los artículos también el artículo, antes de que React arranque, y el HTML prerenderizado se queda en pantalla hasta que React muestra lo mismo. Tercero, la fuente web entraba después del primer pintado y movía el texto. Con la fuente precargada y font-display en optional, o llega a tiempo o no se usa en esa visita. Todo junto bajó el mayor pintado en móvil de la portada de unos 2,5 a 1,1 segundos.
<!-- index.html: runs before CSS, fonts and the app bundle -->
<script>
(function () {
var l = location, p = l.pathname, s = new URLSearchParams(l.search), q = s.get("lang"), w;
if (/^\/(de|es)(\/|$)/.test(p)) return;
var ok = function (x) { return x === "en" || x === "de" || x === "es"; };
try { w = ok(q) ? q : p === "/" ? [localStorage.getItem("dominik-lang"), (navigator.language || "").slice(0, 2)].filter(ok)[0] : ""; } catch (e) {}
if (w !== "de" && w !== "es") return;
s.delete("lang");
var r = s.toString();
l.replace("/" + w + (p === "/" ? "" : p) + (r ? "?" + r : "") + l.hash);
})();
</script>
<!-- staticwebapp.config.json: script-src 'self' 'sha256-…' -->4. Una guía de IA que hace streaming en un host estático
La guía de abajo a la derecha es mi propio motor de chat, el que hay detrás del producto Helptempo: un componente web en un Shadow DOM, para que el CSS de la web no pueda romperlo, y un único controlador de servidor que funciona en cualquier host. En esta web, el controlador corre como una Azure Function gestionada. Responde con el archivo de conocimiento generado más los títulos de la página en la que está el visitante, con Gemini 3.5 Flash y Gemini 3.5 Flash-Lite como respaldo, que solo entra antes de que se haya enviado la primera palabra. Las respuestas preparadas hacen instantáneas las preguntas sugeridas, y un límite por visitante mantiene viva la clave gratuita.
El streaming necesitó el modelo de programación v4 de Azure Functions con los streams HTTP activados. La sorpresa fue una sola cabecera: con Content-Type 'text/event-stream; charset=utf-8', Azure Static Web Apps retenía toda la respuesta hasta que estaba completa; con exactamente 'text/event-stream', cada trozo llegaba según se escribía. Lo aislé con ocho variantes en un entorno de preview antes de tocar producción. Ahora las primeras palabras aparecen tras unos 1,2 a 1,5 segundos en vez de la respuesta completa tras 2,5 a 3.
// api/chat/index.js — Azure Functions v4 on Static Web Apps
const { app } = require("@azure/functions");
app.setup({ enableHttpStream: true });
app.http("chat", { methods: ["GET", "POST"], authLevel: "anonymous", route: "chat",
handler: async (req) => (await chat).azureFunctions(req) });
// in the engine: exactly this content type, no charset suffix
// "Content-Type": "text/event-stream"5. Comprobaciones que se niegan a desplegar
Cada push a main compila la web, comprueba los tipos y ejecuta tests de extremo a extremo contra los archivos compilados antes de desplegar nada: cada ruta en cada idioma, en ancho de móvil y de escritorio, debe tener exactamente un h1, ningún scroll horizontal, ninguna imagen rota y ningún error de consola. El cuestionario, el cambio de idioma, los enlaces compartidos de las herramientas y la secuencia de arranque tienen sus propias comprobaciones, entre ellas una que ralentiza el JavaScript y falla si el título prerenderizado desaparece. La API del chat tiene sus propias pruebas, y el conocimiento de la guía de IA se prueba con 30 preguntas de visitantes con datos que debe decir y que no debe decir.
Tras el despliegue, la web avisa a IndexNow de qué URL han cambiado. Fuera de los despliegues, una tarea programada comprueba cada hora que responden todas las páginas, artículos, PDF de guías, feeds y el chat, y una tarea semanal revisa cada enlace a fuentes y cada vídeo de los artículos. Un enlace roto o una página caída pone en rojo un workflow y llega a mi bandeja antes de que lo encuentre un visitante.
6. Lo que cuesta, y lo que reutilizaría
El alojamiento es el plan gratuito de Azure Static Web Apps con sus funciones gestionadas, y la guía funciona con una clave gratuita de Gemini y un modelo de respaldo, así que el coste de funcionamiento es casi cero. Las concesiones son honestas: la cuota gratuita del modelo a veces se agota durante el día y responde el respaldo, y un host estático implica que el formulario de contacto envía a un servicio de formularios en vez de a un backend propio.
Lo que reutilizaría en cualquier web de empresa: prerenderizar en vez de renderizar en servidor cuando el contenido cambia unas pocas veces al día, generar cada archivo legible por máquinas desde una sola fuente de datos, medir antes de optimizar y dejar que el pipeline se niegue a desplegar en vez de confiar en que la gente recuerde las comprobaciones. El motor de chat es reutilizable por diseño; por eso se convirtió en un producto.
Conclusión
Una web rápida y multilingüe con una guía de IA en directo no necesita una granja de servidores: prerenderiza todo, protege el primer pintado, haz streaming solo de lo que lo necesita y deja que el pipeline diga que no.
Proyectos reales relacionados
Fuentes y guías oficiales
Más artículos
- 3.025 pull requests en diez meses: un proceso de entrega con IA, desde dentro
- La Ley de IA de la UE para medianas empresas: qué te aplica y desde cuándo
- FinOps para el gasto en IA: tokens, caché, enrutado y presupuestos que aguantan
- Hacer visible una red de restaurantes en las respuestas de la IA: un proyecto GEO paso a paso
- Costes de Kubernetes en EKS y GKE: el mismo método, otras palancas
- Una landing zone para empresas medianas, en Terraform o Bicep: las piezas que importan
- El manual de migración de sistemas heredados: oleadas, ejecución en paralelo y recuentos y checksums idénticos
- Seguridad de MCP a fondo: dejar entrar a los asistentes de IA en tu producto sin abrir las puertas
- Monitorizar resultados de negocio: paneles en verde, caja vacía
- Agentes de IA por teléfono: la arquitectura y los tropiezos que ninguna demo enseña
- Pruebas de restauración: un backup que nunca has restaurado es solo una esperanza
- Respuestas de IA en streaming en Azure Static Web Apps: la cabecera que lo retenía todo
- Construir un asistente de IA para WhatsApp de principio a fin: lo que hace falta más allá de la demo
- Sistemas air-gapped: software crítico sin conexión a internet
- IA en producción en AWS: Amazon Bedrock y AgentCore en una configuración privada
- IA en producción con Microsoft Foundry: una configuración de referencia privada para agentes
- /goal, /loop o rutinas: deja de vigilar a tu agente de programación
- Los nuevos planes de Claude: Max, Ultra y lo que significan para los equipos
- OpenAI Codex a finales de 2026: goal mode, dots y cómo blindarlo
- ¿GitHub Copilot, Claude Code o Codex? Cómo elegiría para una empresa
- Fundamentos de GEO: cómo aparecer en las respuestas de la IA
- IA en producción en Google Cloud: de Vertex AI a Gemini Enterprise Agent Platform
- LLM locales en una red air-gapped: IA para tus datos más sensibles
- Quiero ejecutar un LLM local: ¿qué servidor compro?
- MiniMax M3: ¿un modelo puntero que puedes ejecutar tú mismo?
- Investigación de prompts a gran escala con un modelo abierto
- SEO y GEO juntos: una auditoría práctica en 6 pasos
- Dimensionar bien Kubernetes: el método que llevó un clúster de 7 a 4 nodos
- IA privada para datos sensibles: un LLM local que supera la revisión de seguridad
- Del prototipo con IA a producción: las ocho capas que añado, en este orden
