TL;DR
Pretext.js es una biblioteca TypeScript de cero dependencias que mide y posiciona texto multilínea mediante pura aritmética en lugar de operaciones DOM. Elimina los reflujos síncronos forzados, ofrece una medición de texto ~500 veces más rápida que getBoundingClientRect() y es compatible con los principales sistemas de escritura del planeta. Si creas desplazadores virtuales, interfaces de usuario de chat o cuadrículas de datos, esta biblioteca resuelve un problema que los navegadores han ignorado durante 30 años.
Introducción
Cada vez que tu JavaScript llama a getBoundingClientRect() o lee offsetHeight, el navegador detiene todo. Borra los cambios de estilo pendientes, recalcula el diseño y fuerza una pasada de renderizado completa. Esto se llama reflujo síncrono forzado, y es la operación más costosa que un navegador puede realizar.
Ahora multiplica eso por 1.000 burbujas de chat en una lista virtual. O 10.000 filas en una cuadrícula de datos. ¿El resultado? Fotogramas perdidos, tirones y usuarios que piensan que tu aplicación está rota.
Cheng Lou, el desarrollador detrás de react-motion (más de 21.700 estrellas en GitHub) y colaborador principal de React y ReasonML en Meta, construyó Pretext.js para solucionar esto. La biblioteca se lanzó en marzo de 2026, alcanzó más de 14.000 estrellas en GitHub en días y desató uno de los hilos de Hacker News más grandes del año.
Este artículo desglosa qué hace Pretext.js, cómo funciona internamente, cuándo deberías usarlo y dónde se queda corto. Sabrás si esta biblioteca pertenece a tu stack.
¿Qué es Pretext.js?
Pretext.js es un motor de diseño de texto puro JavaScript/TypeScript. Mide y posiciona texto multilínea enteramente a través de aritmética; sin getBoundingClientRect(), sin offsetHeight, sin reflujo, sin tirones.

La idea central es simple. En lugar de preguntarle al navegador "¿qué altura tiene este texto?" (lo que obliga al navegador a renderizarlo primero), Pretext.js calcula la respuesta matemáticamente utilizando métricas de fuente de la API Canvas.
Aquí está toda la superficie de la API:
import { prepare, layout } from '@chenglou/pretext';
// Paso 1: Preparar texto (una vez, cacheable)
const handle = prepare('Hola, pretext.js', '16px "Inter"');
// Paso 2: Diseñar a cualquier ancho (aritmética pura, microsegundos)
const { height, lineCount } = layout(handle, 400, 24);
Eso es todo. Dos funciones. Una mide segmentos de texto y los almacena en caché. La otra hace aritmética para calcular el diseño. La llamada a prepare() es la única operación que toca el navegador (a través de measureText() de Canvas). Después de eso, layout() es pura matemática.
Por qué esto es importante para aplicaciones intensivas en API
Si estás creando aplicaciones que consumen respuestas de API en streaming; piensa en asistentes de IA, paneles de control en tiempo real o editores colaborativos; necesitas saber la altura del texto entrante antes de renderizarlo. Sin eso, tu desplazador virtual tartamudea, tu interfaz de chat salta y tus usuarios se dan cuenta.
Pretext.js te da esa altura en microsegundos en lugar de milisegundos. La diferencia se acumula rápidamente.
El problema que resuelve Pretext.js
Para entender por qué existe esta biblioteca, necesitas comprender qué sucede cuando JavaScript lee propiedades de diseño.
Reflujo síncrono forzado explicado
Cuando escribes este código:
const elements = document.querySelectorAll('.text-block');
elements.forEach(el => {
const height = el.getBoundingClientRect().height; // ¡REFLUJO!
// usa la altura para el posicionamiento...
});
Cada llamada a getBoundingClientRect() fuerza al navegador a:
- Pausar la ejecución de JavaScript
- Borrar todos los cambios de estilo pendientes
- Recalcular el diseño para todo el documento (o subárbol)
- Devolver el valor calculado
Esto se llama "layout thrashing" (agitación del diseño). En un bucle que mide 1.000 elementos, el navegador realiza 1.000 recalculaciones completas del diseño. ¿El costo? Aproximadamente 94 milisegundos, lo que significa 6 fotogramas perdidos a 60 fps.
El problema del desplazamiento virtual
Las bibliotecas de desplazamiento virtual (como react-window o tanstack-virtual) necesitan saber la altura de cada elemento para calcular las posiciones de desplazamiento. Para elementos de altura fija, esto es trivial. Para contenido de texto de altura variable, es una pesadilla.
La solución estándar es renderizar elementos fuera de pantalla, medirlos y luego posicionarlos. Esto funciona, pero anula el propósito del desplazamiento virtual; estás renderizando nodos DOM que intentas evitar renderizar.
Algunas bibliotecas estiman las alturas y las corrigen después del renderizado, causando saltos visibles. Otras obligan a los desarrolladores a especificar alturas fijas, limitando lo que se puede mostrar.
Pretext.js elimina toda esta categoría de soluciones alternativas. Calcula la altura exacta del texto antes de que exista cualquier nodo DOM.
Números reales
Pretext.js publicó resultados de pruebas comparativas en su sitio:
| Enfoque | 1.000 bloques de texto | 500 bloques de texto |
|---|---|---|
DOM (getBoundingClientRect) |
~94ms (6 fotogramas perdidos) | ~47ms |
Pretext.js (layout()) |
~2ms | ~0.09ms |
| Diferencia de velocidad | ~47x más rápido | ~500x más rápido |
La mejora de velocidad es más dramática con lotes más pequeños porque la sobrecarga por llamada de la medición DOM permanece constante, mientras que el costo aritmético de Pretext escala linealmente.
Cómo funciona Pretext.js internamente
La biblioteca opera en tres fases distintas. Comprenderlas te ayuda a optimizar cómo la usas.
Fase 1: Segmentación de texto
Cuando llamas a prepare(), Pretext.js primero normaliza tu texto de entrada. Maneja los espacios en blanco, aplica las reglas de salto de línea Unicode (UAX #14) y segmenta el texto en unidades divisibles.
Aquí es donde entra en juego el soporte multilingüe. El motor de segmentación maneja correctamente:
- Caracteres CJK (chino, japonés, coreano): Cada carácter es un punto de interrupción válido
- Árabe y hebreo: Texto de derecha a izquierda con marcadores bidireccionales
- Tailandés: Sin espacios entre palabras, requiere segmentación basada en diccionarios
- Hindi/Devanagari: Complejas consonantes conjuntas y ligaduras
- Emoji: Maneja correctamente secuencias de emoji con múltiples puntos de código (banderas, tonos de piel, secuencias ZWJ)
- Guiones suaves: Respeta las oportunidades de salto
­
Fase 2: Medición de Canvas
Después de la segmentación, Pretext.js alimenta cada segmento a través de la API measureText() de Canvas. Esta es la única llamada al navegador que hace la biblioteca, y es rápida porque la medición de texto de Canvas no activa el reflujo del diseño.
// Interno: cómo Pretext mide el texto
const ctx = offscreenCanvas.getContext('2d');
ctx.font = '16px "Inter"';
const metrics = ctx.measureText('Hola'); // ¡Sin reflujo!
const width = metrics.width; // Ancho de avance del glifo
Las mediciones se almacenan en caché por combinación de segmento y fuente. Si llamas a prepare() con el mismo texto y fuente dos veces, la segunda llamada reutiliza los datos almacenados en caché.
Fase 3: Diseño aritmético puro
La función layout() toma los anchos de segmento almacenados en caché y un ancho de contenedor, luego calcula los saltos de línea utilizando un algoritmo codicioso:
- Suma los anchos de los segmentos hasta que el total exceda el ancho del contenedor
- Salto a una nueva línea
- Repite hasta que todos los segmentos estén colocados
- Multiplica el número de líneas por el interlineado para obtener la altura total
Sin DOM. Sin Canvas. Pura adición y comparación.
Por eso layout() es tan rápido; está haciendo la misma matemática que escribirías en papel con una regla y una calculadora.
El patrón de manejador reutilizable
Una de las mejores decisiones de diseño en Pretext.js es que prepare() devuelve un manejador reutilizable. Una sola llamada a prepare() funciona en todos los anchos de contenedor:
const handle = prepare(textoArticuloLargo, '16px "Inter"');
// Calcula la altura para móvil, tablet y escritorio en microsegundos
const mobile = layout(handle, 375, 24); // { height: 2400, lineCount: 100 }
const tablet = layout(handle, 768, 24); // { height: 1200, lineCount: 50 }
const desktop = layout(handle, 1200, 24); // { height: 720, lineCount: 30 }
Este patrón es perfecto para cálculos de diseño responsivo. Mides una vez y maquetas a cualquier ancho al instante.
Casos de uso prácticos
1. Desplazamiento virtual con texto de altura variable
Este es el caso de uso principal. Así es como integrarías Pretext.js con un desplazador virtual:
import { prepare, layout } from '@chenglou/pretext';
interface TextItem {
id: string;
content: string;
}
function computeHeights(items: TextItem[], containerWidth: number) {
return items.map(item => {
const handle = prepare(item.content, '14px "Inter"');
const { height } = layout(handle, containerWidth, 20);
return { id: item.id, height: height + 32 }; // +32 para padding
});
}
// 10.000 elementos medidos en ~4ms
const heights = computeHeights(mensajesChat, 600);
Sin renderizado fuera de pantalla. Sin estimación de altura. Sin saltos visibles cuando los elementos se desplazan a la vista.
2. Interfaces de chat de IA
Los asistentes de IA transmiten respuestas token por token. Cada nuevo token puede cambiar el número de líneas, desplazando todo lo que está debajo. Con la medición DOM tradicional, cada actualización de token activa un reflujo.
Con Pretext.js, recalculas la altura después de que llega cada fragmento sin tocar el DOM:
let streamedText = '';
const font = '15px "SF Pro"';
socket.on('token', (token: string) => {
streamedText += token;
const handle = prepare(streamedText, font);
const { height } = layout(handle, bubbleWidth, 22);
// Actualiza la posición del desplazador virtual sin medición DOM
scroller.updateItemHeight(messageId, height + padding);
});
3. Cuadrículas de datos con columnas de texto
Las aplicaciones estilo hoja de cálculo necesitan autoajuste de columnas. Medir miles de valores de celdas a través del DOM es costoso. Pretext.js lo hace rápido:
function computeColumnWidth(values: string[], font: string, padding: number) {
let maxWidth = 0;
for (const value of values) {
const handle = prepare(value, font);
// Diseño con ancho infinito = una sola línea = ancho de texto natural
const { height } = layout(handle, Infinity, 20);
// Usa el seguimiento interno del ancho del handle para dimensionar la columna
maxWidth = Math.max(maxWidth, /* ancho calculado */);
}
return maxWidth + padding;
}
4. Feeds de contenido multilingües
Los feeds de redes sociales con contenido de scripts mixtos (publicaciones chinas seguidas de respuestas árabes seguidas de comentarios coreanos) son notoriamente difíciles de virtualizar porque cada script tiene diferentes reglas de salto de línea.
Pretext.js los maneja todos con la misma API:
const posts = [
{ text: 'Esta biblioteca cambió todo', lang: 'es' },
{ text: 'Texto RTL con diseño bidireccional correcto', lang: 'ar' },
{ text: 'El texto CJK obtiene saltos correctos a nivel de carácter', lang: 'zh' },
];
// Misma API, resultados correctos para cada script
posts.forEach(post => {
const handle = prepare(post.text, '16px system-ui');
const { height } = layout(handle, 400, 24);
});
Probando tu diseño de texto con Apidog
Cuando estás creando interfaces de usuario con mucho texto respaldadas por APIs, lograr el diseño correcto es solo la mitad de la batalla. También necesitas verificar que las respuestas de la API que alimentan tus componentes de texto entreguen los datos correctos, en el formato correcto, a la velocidad correcta.

Apidog lo hace sencillo. Puedes simular respuestas de API en streaming para probar cómo tu integración de Pretext.js maneja la carga progresiva de texto. Configura escenarios de prueba con diferentes longitudes de texto, idiomas y casos extremos de Unicode, luego verifica que tu desplazador virtual se comporte correctamente antes de implementarlo.
Para equipos que construyen productos de chat de IA, el entorno de pruebas de API de Apidog te permite:
- Simular respuestas en streaming con texto fragmentado para emular la salida real de LLM
- Probar con cargas útiles multilingües para detectar errores de diseño antes que los usuarios
- Validar esquemas de respuesta para confirmar que los campos de texto contienen el formato esperado
- Ejecutar suites de pruebas automatizadas que cubren tus casos extremos de renderizado de texto
Esto es importante porque una biblioteca de diseño de texto es tan buena como los datos que fluyen hacia ella. Las respuestas de API deficientes producen diseños deficientes, sin importar cuán rápido funcione tu motor de medición.
Limitaciones y críticas conocidas
Pretext.js no es perfecto. El hilo de Hacker News sacó a la luz varias preocupaciones válidas que vale la pena conocer antes de adoptarlo.
Casos extremos de precisión de renderizado
Varios usuarios informaron que el texto se extendía más allá de los cuadros delimitadores en las demostraciones de Safari y Chrome. La aritmética de la biblioteca puede divergir del diseño nativo del navegador en escenarios específicos:
- Fuentes con pares de kerning inusuales
- Texto con tamaños de fuente mixtos dentro de un solo bloque
- Diferencias de renderizado de subpíxeles entre Canvas y DOM
- Particularidades del modelado de texto específicas del navegador
Estos casos extremos importan menos para el desplazamiento virtual (donde unos pocos píxeles de error son invisibles) y más para la composición tipográfica perfecta en píxeles.
La medición de Canvas no es gratuita
La llamada a prepare() todavía accede al motor de texto de Canvas del navegador. Para aplicaciones que crean miles de manejadores prepare() únicos por fotograma, esto puede convertirse en un cuello de botella. La solución es el almacenamiento en caché y el procesamiento por lotes, pero la biblioteca no impone ninguno de ellos.
Sin soporte para propiedades CSS
Pretext.js mide el texto sin formato con una especificación de fuente. No tiene en cuenta las propiedades CSS que afectan el diseño:
letter-spacingword-spacingtext-indenttext-transformfont-feature-settingsfont-variant
Si el estilo de tu texto depende de estas propiedades CSS, la altura calculada no coincidirá con lo que el navegador renderiza. Tendrías que tenerlas en cuenta manualmente o aceptar la discrepancia.
No reemplaza el renderizado DOM
Pretext.js te dice qué tan alto será el texto. No renderiza el texto por ti. Aún necesitas nodos DOM (o renderizado Canvas/SVG) para mostrar el texto. El valor de la biblioteca está en la fase de medición, no en la fase de renderizado.
Pretext.js vs. enfoques tradicionales
| Característica | Pretext.js | Medición DOM | Alturas estimadas |
|---|---|---|---|
| Velocidad (1K elementos) | ~2ms | ~94ms | ~0ms (sin medición) |
| Precisión | Alta (basada en Canvas) | Perfecta (verdad fundamental) | Baja (heurística) |
| Dependencia del DOM | Ninguna después de prepare() |
Completa | Ninguna |
| Activadores de reflujo | Cero | Uno por medición | Cero |
| Multilingüe | Soporte Unicode completo | Completo (nativo del navegador) | Deficiente (ratios codificados) |
| Soporte de propiedades CSS | Limitado (solo fuente) | Completo | Ninguno |
| Sobrecarga de memoria | Segmentos en caché | Nodos DOM | Mínima |
| Diseños responsivos | Un prepare(), muchos layout() |
Volver a medir por ancho | Volver a estimar por ancho |
La elección correcta depende de tus limitaciones. Si necesitas una precisión perfecta en píxeles y compatibilidad con propiedades CSS, la medición DOM sigue siendo la verdad fundamental. Si necesitas velocidad en miles de elementos y puedes tolerar pequeñas diferencias de subpíxeles, Pretext.js gana por un amplio margen.
Empezando
Instalación
npm install @chenglou/pretext
# o
pnpm add @chenglou/pretext
# o
bun add @chenglou/pretext
Uso básico
import { prepare, layout } from '@chenglou/pretext';
// Medir un párrafo
const handle = prepare(
'Pretext.js calcula el diseño de texto sin tocar el DOM.',
'16px "Inter"'
);
// Obtener la altura en un ancho de contenedor específico
const result = layout(handle, 600, 24);
console.log(result.height); // e.g., 48 (2 líneas x 24px)
console.log(result.lineCount); // e.g., 2
Integración con React
import { prepare, layout } from '@chenglou/pretext';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useMemo, useRef } from 'react';
function VirtualChat({ messages }: { messages: string[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const containerWidth = 600;
const font = '14px "Inter"';
const lineHeight = 20;
const heights = useMemo(() => {
return messages.map(msg => {
const handle = prepare(msg, font);
const { height } = layout(handle, containerWidth, lineHeight);
return height + 24; // padding
});
}, [messages]);
const virtualizer = useVirtualizer({
count: messages.length,
getScrollElement: () => parentRef.current,
estimateSize: (index) => heights[index],
});
return (
<div ref={parentRef} style={{ height: '100vh', overflow: 'auto' }}>
<div style={{ height: virtualizer.getTotalSize(), position: 'relative' }}>
{virtualizer.getVirtualItems().map(virtualRow => (
<div
key={virtualRow.key}
style={{
position: 'absolute',
top: virtualRow.start,
width: containerWidth,
}}
>
{messages[virtualRow.index]}
</div>
))}
</div>
</div>
);
}
Esto te proporciona un chat virtual con alturas de elementos precisas calculadas antes de que cualquier mensaje se renderice en el DOM. Sin estimación, sin saltos de corrección, sin reflujo.
Parque de juegos interactivo
El sitio web de Pretext.js incluye un parque de juegos interactivo en pretextjs.dev/playground donde puedes pegar texto, elegir fuentes, ajustar el ancho del contenedor y ver el cálculo del diseño en tiempo real. Es la forma más rápida de verificar el comportamiento antes de la integración.
Cuándo NO deberías usar Pretext.js
Pretext.js no es la herramienta adecuada para todos los problemas de medición de texto:
- Páginas estáticas con contenido conocido: Si tu texto no cambia y no estás virtualizando, CSS maneja bien el diseño. No se necesita ninguna biblioteca.
- Diseños de impresión perfectos en píxeles: Las diferencias de subpíxeles entre la medición de Canvas y el renderizado DOM importan a la resolución de impresión. Utiliza el DOM como verdad fundamental.
- Estilo de texto CSS pesado: Si dependes de
letter-spacing,text-indentofont-feature-settings, los cálculos de altura divergirán de la salida renderizada. - Renderizado del lado del servidor: Pretext.js depende de la API Canvas, que no está disponible en Node.js sin polyfills como
node-canvas. El soporte del lado del servidor está en la hoja de ruta pero aún no se ha lanzado. - Listas pequeñas y estáticas: Si tienes 50 elementos en una lista, la medición DOM tarda menos de 5 ms. La optimización no vale la pena la dependencia.
Preguntas Frecuentes
¿Está Pretext.js listo para producción?
La biblioteca se lanzó en marzo de 2026 y obtuvo más de 14.000 estrellas en GitHub en cuestión de días. Cheng Lou, el creador, dirige el frontend de Midjourney; un sistema de producción que atiende a millones de usuarios. La suite de pruebas de la biblioteca cubre docenas de idiomas y casos extremos. Dicho esto, es un lanzamiento nuevo. Fija tu versión y prueba con tus fuentes y contenido específicos.
¿Funciona Pretext.js con React, Vue y Svelte?
Sí. Pretext.js es agnóstico a los frameworks. Es una biblioteca TypeScript pura con dos funciones. Llamas a prepare() y layout() donde necesites mediciones de texto; dentro de hooks de React, composables de Vue, stores de Svelte o JavaScript puro.
¿Cómo maneja Pretext.js las fuentes web?
La función prepare() mide el texto usando la fuente que el navegador haya cargado en el momento de la llamada. Si tu fuente web aún no se ha cargado, la medición usará la fuente de respaldo y producirá resultados incorrectos. Asegúrate de que tus fuentes estén cargadas antes de llamar a prepare(). Usa la API de carga de fuentes (document.fonts.ready) para verificarlo.
¿Puedo usar Pretext.js para renderizado Canvas o SVG?
Sí. La biblioteca calcula el diseño de texto que es agnóstico al objetivo de renderizado. Puedes usar las alturas calculadas y los saltos de línea para posicionar texto en Canvas 2D, WebGL, SVG o DOM. El sitio web de Pretext.js muestra ejemplos de todos estos objetivos de renderizado.
¿Admite idiomas RTL (de derecha a izquierda)?
Sí. Pretext.js maneja el árabe, el hebreo y otros scripts RTL con el soporte adecuado para texto bidireccional. También maneja correctamente texto de dirección mixta (por ejemplo, texto árabe con palabras en inglés incrustadas).
¿Cuál es el tamaño del paquete?
15KB minificado con cero dependencias. No se requieren polyfills. La biblioteca utiliza solo las API de navegador estándar (measureText() de Canvas e Intl.Segmenter donde esté disponible).
¿Qué tan preciso es en comparación con la medición DOM?
Para la mayoría del contenido de texto, Pretext.js coincide con el diseño DOM dentro de 1-2 píxeles. La precisión depende de la fuente y las propiedades CSS que uses. Propiedades como letter-spacing y word-spacing no se tienen en cuenta, por lo que si las usas, espera mayores diferencias. Para el desplazamiento virtual, donde unos pocos píxeles de error son invisibles, la precisión es más que suficiente.
¿Puede Pretext.js medir texto con estilo (negrita, cursiva, tamaños mixtos)?
Cada llamada a prepare() toma una única especificación de fuente. Para texto con estilos mixtos (palabras en negrita dentro de texto normal), tendrías que segmentar el texto tú mismo y crear manejadores separados para cada estilo. Esta es una limitación conocida que podría abordarse en futuras versiones.
Conclusión
Pretext.js resuelve un problema que los navegadores han ignorado durante tres décadas: medición rápida y precisa de texto sin reflujo DOM. Para los desarrolladores que construyen desplazadores virtuales, interfaces de usuario de chat, cuadrículas de datos o cualquier interfaz que necesite medir miles de bloques de texto, esta biblioteca reemplaza toda una categoría de soluciones alternativas con dos llamadas a funciones.
La biblioteca no es una panacea. No es compatible con propiedades de texto CSS más allá de la especificación de fuente, tiene pequeñas diferencias de precisión de subpíxeles y aún no funciona del lado del servidor. Pero para su caso de uso principal; precalcular alturas de texto para listas virtualizadas; nada más se le acerca.
¿Listo para construir interfaces de usuario con mucho texto más rápidas? Comienza probando tus endpoints de API con Apidog para asegurarte de que tu capa de datos sea sólida, luego inserta Pretext.js en tu pipeline de renderizado.
