Em resumo
Pretext.js é uma biblioteca TypeScript de dependência zero que mede e posiciona texto de várias linhas através de pura aritmética, em vez de operações DOM. Ela elimina reflows síncronos forçados, oferece uma medição de texto ~500x mais rápida do que getBoundingClientRect(), e suporta todos os principais sistemas de escrita do planeta. Se você constrói scrollers virtuais, UIs de chat ou grades de dados, esta biblioteca resolve um problema que os navegadores ignoraram por 30 anos.
Introdução
Toda vez que seu JavaScript chama getBoundingClientRect() ou lê offsetHeight, o navegador para tudo. Ele limpa as alterações de estilo pendentes, recalcula o layout e força uma passagem de renderização completa. Isso é chamado de reflow síncrono forçado, e é a operação mais cara que um navegador pode realizar.
Agora multiplique isso por 1.000 balões de chat em uma lista virtual. Ou 10.000 linhas em uma grade de dados. O resultado? Quadros perdidos, travamentos e usuários que pensam que seu aplicativo está com problemas.
Cheng Lou, o desenvolvedor por trás de react-motion (mais de 21.700 estrelas no GitHub) e um colaborador central para React e ReasonML na Meta, construiu o Pretext.js para corrigir isso. A biblioteca foi lançada em março de 2026, alcançou mais de 14.000 estrelas no GitHub em dias e gerou um dos maiores tópicos do Hacker News do ano.
Este artigo detalha o que o Pretext.js faz, como funciona por baixo do capô, quando você deve usá-lo e onde ele fica aquém. Você sairá sabendo se esta biblioteca pertence à sua pilha de tecnologia.
O que é o Pretext.js?
Pretext.js é um motor de layout de texto puro JavaScript/TypeScript. Ele mede e posiciona texto de várias linhas inteiramente através de aritmética; sem getBoundingClientRect(), sem offsetHeight, sem reflow, sem travamentos.

A ideia central é simples. Em vez de perguntar ao navegador “qual a altura deste texto?” (o que força o navegador a renderizá-lo primeiro), o Pretext.js calcula a resposta matematicamente usando métricas de fonte da API Canvas.
Aqui está toda a superfície da API:
import { prepare, layout } from '@chenglou/pretext';
// Step 1: Prepare text (one-time, cacheable)
const handle = prepare('Hello, pretext.js', '16px "Inter"');
// Step 2: Layout at any width (pure arithmetic, microseconds)
const { height, lineCount } = layout(handle, 400, 24);
É isso. Duas funções. Uma mede segmentos de texto e os armazena em cache. A outra faz aritmética para calcular o layout. A chamada prepare() é a única operação que toca o navegador (via Canvas measureText()). Depois disso, layout() é pura matemática.
Por que isso é relevante para aplicações que dependem muito de APIs
Se você está construindo aplicativos que consomem respostas de API de streaming; pense em assistentes de IA, painéis em tempo real ou editores colaborativos; você precisa saber a altura do texto recebido antes de renderizá-lo. Sem isso, seu scroller virtual trava, sua UI de chat pula, e seus usuários percebem.
Pretext.js oferece essa altura em microssegundos em vez de milissegundos. A diferença se acumula rapidamente.
O problema que o Pretext.js resolve
Para entender por que esta biblioteca existe, você precisa entender o que acontece quando o JavaScript lê propriedades de layout.
Reflow síncrono forçado explicado
Quando você escreve este código:
const elements = document.querySelectorAll('.text-block');
elements.forEach(el => {
const height = el.getBoundingClientRect().height; // REFLOW!
// use height for positioning...
});
Cada chamada getBoundingClientRect() força o navegador a:
- Pausar a execução do JavaScript
- Limpar todas as alterações de estilo pendentes
- Recalcular o layout para o documento inteiro (ou subárvore)
- Retornar o valor calculado
Isso é chamado de “layout thrashing” (esgotamento de layout). Em um loop que mede 1.000 elementos, o navegador realiza 1.000 recálculos de layout completos. O custo? Aproximadamente 94 milissegundos, o que significa 6 quadros perdidos a 60fps.
O problema do scroll virtual
As bibliotecas de scroll virtual (como react-window ou tanstack-virtual) precisam saber a altura de cada item para calcular as posições de rolagem. Para itens de altura fixa, isso é trivial. Para conteúdo de texto de altura variável, é um pesadelo.
A solução alternativa padrão é renderizar itens fora da tela, medi-los e depois posicioná-los. Isso funciona, mas anula o propósito do scroll virtual; você está renderizando nós DOM que está tentando evitar renderizar.
Algumas bibliotecas estimam alturas e as corrigem após a renderização, causando saltos visíveis. Outras forçam os desenvolvedores a especificar alturas fixas, limitando o que pode ser exibido.
Pretext.js elimina toda esta categoria de soluções alternativas. Você calcula a altura exata do texto antes que qualquer nó DOM exista.
Números reais
Pretext.js publicou resultados de benchmark em seu site:
| Abordagem | 1.000 blocos de texto | 500 blocos de texto |
|---|---|---|
DOM (getBoundingClientRect) |
~94ms (6 quadros perdidos) | ~47ms |
Pretext.js (layout()) |
~2ms | ~0.09ms |
| Diferença de velocidade | ~47x mais rápido | ~500x mais rápido |
A melhoria de velocidade é mais dramática com lotes menores porque o custo fixo por chamada da medição DOM permanece constante, enquanto o custo aritmético do Pretext escala linearmente.
Como o Pretext.js funciona por baixo do capô
A biblioteca opera em três fases distintas. Entendê-las ajuda você a otimizar a forma como a usa.
Fase 1: Segmentação de texto
Quando você chama prepare(), o Pretext.js primeiro normaliza seu texto de entrada. Ele lida com espaços em branco, aplica as regras de quebra de linha Unicode (UAX #14) e segmenta o texto em unidades quebráveis.
É aqui que entra o suporte multilíngue. O motor de segmentação lida corretamente com:
- **Caracteres CJK** (chinês, japonês, coreano): Cada caractere é um ponto de quebra válido
- **Árabe e hebraico**: Texto da direita para a esquerda com marcadores bidirecionais
- **Tailandês**: Sem espaços entre as palavras, requer segmentação baseada em dicionário
- **Hindi/Devanágari**: Consoantes e ligaduras conjuntas complexas
- **Emoji**: Lida corretamente com sequências de emoji de múltiplos pontos de código (bandeiras, tons de pele, sequências ZWJ)
- **Hífens suaves**: Respeita as oportunidades de quebra de
­
Fase 2: Medição do Canvas
Após a segmentação, o Pretext.js alimenta cada segmento através da API measureText() do Canvas. Esta é a única chamada ao navegador que a biblioteca faz, e é rápida porque a medição de texto do Canvas não aciona o reflow de layout.
// Internal: how Pretext measures text
const ctx = offscreenCanvas.getContext('2d');
ctx.font = '16px "Inter"';
const metrics = ctx.measureText('Hello'); // No reflow!
const width = metrics.width; // Glyph advance width
As medições são armazenadas em cache por segmento e combinação de fonte. Se você chamar prepare() com o mesmo texto e fonte duas vezes, a segunda chamada reutiliza os dados em cache.
Fase 3: Layout puramente aritmético
A função layout() recebe as larguras dos segmentos em cache e uma largura de contêiner, e então calcula as quebras de linha usando um algoritmo guloso:
- Somar as larguras dos segmentos até que o total exceda a largura do contêiner
- Quebrar para uma nova linha
- Repetir até que todos os segmentos estejam posicionados
- Multiplicar a contagem de linhas pela altura da linha para obter a altura total
Sem DOM. Sem Canvas. Pura adição e comparação.
É por isso que layout() é tão rápido; ele está fazendo a mesma matemática que você faria no papel com uma régua e uma calculadora.
O padrão de identificador reutilizável
Uma das melhores decisões de design no Pretext.js é que prepare() retorna um identificador reutilizável. Uma única chamada prepare() funciona em todas as larguras de contêiner:
const handle = prepare(longArticleText, '16px "Inter"');
// Compute height for mobile, tablet, and desktop in microseconds
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 padrão é perfeito para cálculos de design responsivo. Você mede uma vez e organiza em qualquer largura instantaneamente.
Casos de uso práticos
1. Scroll virtual com texto de altura variável
Este é o caso de uso principal. Veja como você integraria o Pretext.js com um scroller 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 for padding
});
}
// 10,000 items measured in ~4ms
const heights = computeHeights(chatMessages, 600);
Sem renderização fora da tela. Sem estimativa de altura. Sem saltos visíveis quando os itens aparecem na visualização.
2. Interfaces de chat com IA
Assistentes de IA transmitem respostas token por token. Cada novo token pode alterar a contagem de linhas, deslocando tudo abaixo dele. Com a medição DOM tradicional, cada atualização de token aciona um reflow.
Com o Pretext.js, você recalcula a altura após a chegada de cada bloco sem tocar no 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);
// Update virtual scroller position without DOM measurement
scroller.updateItemHeight(messageId, height + padding);
});
3. Grades de dados com colunas de texto
Aplicativos estilo planilha precisam de autoajuste de coluna. Medir milhares de valores de célula através do DOM é caro. O Pretext.js torna isso rápido:
function computeColumnWidth(values: string[], font: string, padding: number) {
let maxWidth = 0;
for (const value of values) {
const handle = prepare(value, font);
// Layout with infinite width = single line = natural text width
const { height } = layout(handle, Infinity, 20);
// Use handle's internal width tracking for column sizing
maxWidth = Math.max(maxWidth, /* computed width */);
}
return maxWidth + padding;
}
4. Feeds de conteúdo multilíngue
Feeds de mídias sociais com conteúdo de scripts mistos (postagens chinesas seguidas por respostas árabes seguidas por comentários coreanos) são notoriamente difíceis de virtualizar porque cada script possui regras de quebra de linha diferentes.
Pretext.js lida com todos eles com a mesma API:
const posts = [
{ text: 'This library changed everything', lang: 'en' },
{ text: 'RTL text with correct bidirectional layout', lang: 'ar' },
{ text: 'CJK text gets proper character-level breaks', lang: 'zh' },
];
// Mesma API, resultados corretos para cada script
posts.forEach(post => {
const handle = prepare(post.text, '16px system-ui');
const { height } = layout(handle, 400, 24);
});
Testando seu layout de texto com Apidog
Quando você está construindo UIs com muito texto, apoiadas por APIs, acertar o layout é apenas metade da batalha. Você também precisa verificar se as respostas da API que alimentam seus componentes de texto entregam os dados corretos, no formato certo e na velocidade certa.

**Apidog** torna isso simples. Você pode simular respostas de API de streaming para testar como sua integração Pretext.js lida com o carregamento progressivo de texto. Configure cenários de teste com diferentes comprimentos de texto, idiomas e casos de borda Unicode, e então verifique se seu scroller virtual se comporta corretamente antes de implantar.
Para equipes que constroem produtos de chat com IA, o ambiente de teste de API do Apidog permite que você:
- **Simular respostas de streaming** com texto em blocos para simular a saída real do LLM
- **Testar com payloads multilíngues** para identificar bugs de layout antes que os usuários os encontrem
- **Validar esquemas de resposta** para confirmar que os campos de texto contêm o formato esperado
- **Executar conjuntos de testes automatizados** que cobrem seus casos de borda de renderização de texto
Isso é importante porque uma biblioteca de layout de texto é tão boa quanto os dados que fluem para ela. Respostas de API ruins produzem layouts ruins, independentemente da velocidade com que seu motor de medição funciona.
Limitações e críticas conhecidas
Pretext.js não é perfeito. O tópico no Hacker News revelou várias preocupações válidas que valem a pena conhecer antes de adotá-lo.
Casos de borda de precisão de renderização
Múltiplos usuários relataram texto se estendendo para além das caixas delimitadoras em demos do Safari e Chrome. A aritmética da biblioteca pode divergir do layout nativo do navegador em cenários específicos:
- Fontes com pares de kerning incomuns
- Texto com tamanhos de fonte mistos dentro de um único bloco
- Diferenças de renderização subpixel entre Canvas e DOM
- Peculiaridades de formatação de texto específicas do navegador
Esses casos de borda importam menos para o scroll virtual (onde alguns pixels de erro são invisíveis) e mais para a tipografia pixel-perfect.
A medição do Canvas não é gratuita
A chamada prepare() ainda aciona o motor de texto do Canvas do navegador. Para aplicações que criam milhares de identificadores prepare() únicos por quadro, isso pode se tornar um gargalo. A solução é cache e agrupamento, mas a biblioteca não impõe nenhum dos dois.
Sem suporte a propriedades CSS
Pretext.js mede texto bruto com uma especificação de fonte. Ele não leva em conta as propriedades CSS que afetam o layout:
letter-spacingword-spacingtext-indenttext-transformfont-feature-settingsfont-variant
Se o estilo do seu texto depende dessas propriedades CSS, a altura calculada não corresponderá ao que o navegador renderiza. Você precisaria considerar isso manualmente ou aceitar a discrepância.
Não substitui a renderização do DOM
Pretext.js informa a altura do texto. Ele não renderiza o texto para você. Você ainda precisa de nós DOM (ou renderização Canvas/SVG) para exibir o texto. O valor da biblioteca está na fase de medição, não na fase de renderização.
Pretext.js vs. abordagens tradicionais
| Funcionalidade | Pretext.js | Medição DOM | Alturas estimadas |
|---|---|---|---|
| Velocidade (1K itens) | ~2ms | ~94ms | ~0ms (sem medição) |
| Precisão | Alta (baseada em Canvas) | Perfeita (verdade absoluta) | Baixa (heurística) |
| Dependência do DOM | Nenhum após prepare() |
Total | Nenhum |
| Gatilhos de reflow | Zero | Um por medição | Zero |
| Multilíngue | Suporte Unicode total | Total (nativo do navegador) | Ruim (proporções fixas) |
| Suporte a propriedades CSS | Limitado (somente fonte) | Total | Nenhum |
| Custo de memória | Segmentos em cache | Nós DOM | Mínimo |
| Layouts responsivos | Um prepare(), muitos layout() |
Remedir por largura | Reestimar por largura |
A escolha certa depende das suas restrições. Se você precisa de precisão pixel-perfect e suporte a propriedades CSS, a medição DOM ainda é a verdade absoluta. Se você precisa de velocidade em milhares de itens e pode tolerar pequenas diferenças sub-pixel, o Pretext.js ganha por uma ampla margem.
Começando
Instalação
npm install @chenglou/pretext
# or
pnpm add @chenglou/pretext
# or
bun add @chenglou/pretext
Uso básico
import { prepare, layout } from '@chenglou/pretext';
// Measure a paragraph
const handle = prepare(
'Pretext.js computes text layout without touching the DOM.',
'16px "Inter"'
);
// Get height at a specific container width
const result = layout(handle, 600, 24);
console.log(result.height); // por exemplo, 48 (2 linhas x 24px)
console.log(result.lineCount); // por exemplo, 2
Integração com 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; // preenchimento
});
}, [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>
);
}
Isso oferece um chat virtual com alturas de itens precisas calculadas antes que qualquer mensagem seja renderizada no DOM. Sem estimativa, sem saltos de correção, sem reflow.
Playground interativo
O site do Pretext.js inclui um playground interativo em pretextjs.dev/playground onde você pode colar texto, escolher fontes, ajustar a largura do contêiner e ver o cálculo do layout em tempo real. É a maneira mais rápida de verificar o comportamento antes de integrar.
Quando você NÃO deve usar o Pretext.js
Pretext.js não é a ferramenta certa para todos os problemas de medição de texto:
- **Páginas estáticas com conteúdo conhecido**: Se seu texto não muda e você não está virtualizando, o CSS lida bem com o layout. Nenhuma biblioteca é necessária.
- **Layouts de impressão pixel-perfect**: As diferenças sub-pixel entre a medição do Canvas e a renderização do DOM importam na resolução de impressão. Use o DOM como verdade absoluta.
- **Estilização de texto CSS pesada**: Se você depende de
letter-spacing,text-indentoufont-feature-settings, os cálculos de altura divergirão da saída renderizada. - **Renderização server-side**: Pretext.js depende da API Canvas, que não está disponível no Node.js sem polyfills como
node-canvas. O suporte server-side está no roteiro, mas ainda não foi lançado. - **Listas pequenas e estáticas**: Se você tem 50 itens em uma lista, a medição do DOM leva menos de 5ms. A otimização não vale a dependência.
Perguntas Frequentes
O Pretext.js está pronto para produção?
A biblioteca foi lançada em março de 2026 e ganhou mais de 14.000 estrelas no GitHub em poucos dias. Cheng Lou, o criador, gerencia o frontend do Midjourney; um sistema de produção que atende milhões de usuários. A suíte de testes da biblioteca cobre dezenas de idiomas e casos de borda. Dito isso, é um lançamento novo. Fixe sua versão e teste-a com suas fontes e conteúdo específicos.
O Pretext.js funciona com React, Vue e Svelte?
Sim. Pretext.js é agnóstico a frameworks. É uma biblioteca TypeScript pura com duas funções. Você chama prepare() e layout() onde precisar de medições de texto; dentro de hooks React, composables Vue, stores Svelte ou JavaScript puro.
Como o Pretext.js lida com fontes da web?
A função prepare() mede o texto usando qualquer fonte que o navegador tenha carregado no momento da chamada. Se sua fonte da web ainda não tiver sido carregada, a medição usará a fonte de fallback e produzirá resultados incorretos. Certifique-se de que suas fontes estejam carregadas antes de chamar prepare(). Use a API de Carregamento de Fontes (document.fonts.ready) para verificar.
Posso usar o Pretext.js para renderização Canvas ou SVG?
Sim. A biblioteca calcula o layout do texto de forma agnóstica ao alvo de renderização. Você pode usar as alturas e quebras de linha calculadas para posicionar o texto em Canvas 2D, WebGL, SVG ou DOM. O site do Pretext.js mostra exemplos de todos esses alvos de renderização.
Ele suporta idiomas RTL (da direita para a esquerda)?
Sim. Pretext.js lida com árabe, hebraico e outros scripts RTL com suporte adequado a texto bidirecional. Ele também lida corretamente com texto de direção mista (por exemplo, texto árabe com palavras em inglês incorporadas).
Qual é o tamanho do bundle?
15KB minificado com zero dependências. Não são necessários polyfills. A biblioteca usa apenas APIs padrão do navegador (Canvas measureText() e Intl.Segmenter onde disponível).
Quão preciso ele é em comparação com a medição DOM?
Para a maioria do conteúdo de texto, Pretext.js corresponde ao layout DOM com uma diferença de 1-2 pixels. A precisão depende da fonte e das propriedades CSS que você usa. Propriedades como letter-spacing e word-spacing não são contabilizadas, então se você as usar, espere diferenças maiores. Para scroll virtual, onde alguns pixels de erro são invisíveis, a precisão é mais do que suficiente.
Pretext.js pode medir texto estilizado (negrito, itálico, tamanhos mistos)?
Cada chamada prepare() recebe uma única especificação de fonte. Para texto com estilos mistos (palavras em negrito dentro de texto regular), você precisaria segmentar o texto por conta própria e criar identificadores separados para cada execução de estilo. Esta é uma limitação conhecida que pode ser abordada em versões futuras.
Conclusão
Pretext.js resolve um problema que os navegadores ignoraram por três décadas: medição de texto rápida e precisa sem reflow do DOM. Para desenvolvedores que constroem scrollers virtuais, UIs de chat, grades de dados ou qualquer interface que precise medir milhares de blocos de texto, esta biblioteca substitui toda uma categoria de soluções alternativas por duas chamadas de função.
A biblioteca não é uma bala de prata. Ela não suporta propriedades de texto CSS além da especificação de fonte, apresenta pequenas diferenças de precisão sub-pixel e ainda não funciona no lado do servidor. Mas para seu caso de uso alvo; pré-computar alturas de texto para listas virtualizadas; nada chega perto.
**Pronto para construir UIs mais rápidas e com muito texto?** Comece testando seus endpoints de API com **Apidog** para garantir que sua camada de dados esteja sólida, e então adicione o Pretext.js ao seu pipeline de renderização.
