DEV Community

Cover image for Pretext.js : La Bibliothèque de 15KB Qui Accélère la Mise en Page de Texte par 500
Antoine Laurent
Antoine Laurent

Posted on • Originally published at apidog.com

Pretext.js : La Bibliothèque de 15KB Qui Accélère la Mise en Page de Texte par 500

En bref

Pretext.js est une bibliothèque TypeScript sans dépendance qui permet de mesurer et de positionner du texte multiligne grâce à des calculs arithmétiques purs, sans aucune opération DOM. Elle évite les reflows synchrones forcés, accélère la mesure de texte (jusqu'à 500 fois plus rapide que getBoundingClientRect()), et prend en charge tous les principaux systèmes d’écriture. Pour des défilements virtuels, interfaces de chat ou grilles de données, Pretext.js résout un problème ignoré par les navigateurs depuis 30 ans.

Essayez Apidog dès aujourd'hui

Introduction

À chaque fois que votre JavaScript utilise getBoundingClientRect() ou lit offsetHeight, le navigateur interrompt tout, applique les styles en attente et force un rendu complet (reflow synchrone forcé). C'est l'opération la plus coûteuse côté navigateur.

Lorsque cela concerne 1 000 bulles de chat dans une liste virtuelle ou 10 000 lignes dans une grille, vous obtenez des pertes d’images, des saccades et des utilisateurs frustrés.

💡 Les équipes Apidog qui développent des frontends pilotés par API connaissent bien cette problématique : diffuser des données dynamiques tout en gardant une interface fluide est un défi constant si le moteur de layout du navigateur vous freine.

Cheng Lou, créateur de react-motion et contributeur clé de React et ReasonML, a conçu Pretext.js pour répondre à ce problème. Lancée en mars 2026, la bibliothèque a rapidement atteint 14 000+ étoiles sur GitHub et déclenché de gros débats sur Hacker News.

Cet article vous montre comment Pretext.js fonctionne, quand l’utiliser, ses limites, et comment l’intégrer à vos projets.

Qu'est-ce que Pretext.js ?

Pretext.js est un moteur de mise en page de texte pur JavaScript/TypeScript. Il mesure et positionne le texte multiligne sans toucher au DOM, en utilisant uniquement les métriques de police de l’API Canvas.

Exemple Pretext.js

API minimale :

import { prepare, layout } from '@chenglou/pretext';

// 1. Préparez le texte (une fois, cachable)
const handle = prepare('Hello, pretext.js', '16px "Inter"');

// 2. Mesurez la hauteur à une largeur donnée (arithmétique pure)
const { height, lineCount } = layout(handle, 400, 24);
Enter fullscreen mode Exit fullscreen mode

prepare() mesure et segmente le texte (utilise Canvas une seule fois), layout() calcule la répartition sur lignes et la hauteur, sans aucun accès DOM.

Pourquoi c'est crucial pour les apps API-intensive

Streaming d’IA, dashboards temps réel, éditeurs collaboratifs : vous devez connaître la hauteur du texte entrant avant de le rendre, sinon le virtuel scroll saccade et la UX souffre. Pretext.js fournit cette valeur en microsecondes.

Le problème résolu par Pretext.js

Le reflow synchrone forcé

Code classique :

const elements = document.querySelectorAll('.text-block');
elements.forEach(el => {
  const height = el.getBoundingClientRect().height; // REFLOW!
  // Utilisez la hauteur...
});
Enter fullscreen mode Exit fullscreen mode

Chaque appel à getBoundingClientRect() force :

  1. Pause JS
  2. Vidage des styles en attente
  3. Recalcul layout sur tout le document
  4. Renvoi de la valeur

Dans une boucle de 1 000 éléments, c’est 1 000 recalculs : ~94ms, soit 6 frames perdues.

Le défi du défilement virtuel

Pour du contenu à hauteur variable, les solutions classiques :

  • Rendu off-screen pour mesurer (anti-virtuel !)
  • Estimation de hauteur puis correction (sauts visuels)
  • Hauteur fixe (UX limitée)

Pretext.js permet de calculer la hauteur exacte avant la création du moindre nœud DOM.

Benchmarks

Approche 1 000 blocs de texte 500 blocs de texte
DOM (getBoundingClientRect) ~94ms (6 frames) ~47ms
Pretext.js (layout()) ~2ms ~0.09ms
Différence de vitesse ~47x plus rapide ~500x plus rapide

La vitesse s’améliore sur des petits lots car la surcharge DOM reste fixe, alors que Pretext scale linéairement.

Comment Pretext.js fonctionne

Trois phases :

1. Segmentation du texte

prepare() normalise le texte, applique les règles Unicode (UAX #14), segmente en unités sécables. Support multilingue complet :

  • CJK (caractère = point de coupure)
  • Arabe/hébreu (RTL, bidirectionnel)
  • Thaï (segmentation par dictionnaire)
  • Hindi/devanagari, emojis, ­

2. Mesure Canvas

Chaque segment est passé à measureText() (Canvas), sans reflow DOM.

const ctx = offscreenCanvas.getContext('2d');
ctx.font = '16px "Inter"';
const metrics = ctx.measureText('Hello'); // Pas de reflow !
const width = metrics.width;
Enter fullscreen mode Exit fullscreen mode

Les mesures sont mises en cache (texte + police).

3. Mise en page arithmétique pure

layout() assemble les segments sur lignes selon la largeur de conteneur :

  1. Additionne les largeurs jusqu’à dépassement
  2. Saute à la ligne
  3. Répète jusqu’à tout placer
  4. Multiplie le nombre de lignes par la hauteur de ligne

Aucun DOM, aucun Canvas, juste de l’arithmétique.

Modèle de poignée réutilisable

Un seul prepare() pour toutes les largeurs :

const handle = prepare(longArticleText, '16px "Inter"');
const mobile = layout(handle, 375, 24);
const tablet = layout(handle, 768, 24);
const desktop = layout(handle, 1200, 24);
Enter fullscreen mode Exit fullscreen mode

Idéal pour les layouts responsives.

Cas d'utilisation pratiques

1. Défilement virtuel avec texte à hauteur variable

Intégration directe :

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 pour le padding
  });
}

// 10 000 éléments mesurés en ~4ms
const heights = computeHeights(chatMessages, 600);
Enter fullscreen mode Exit fullscreen mode

Pas de rendu off-screen ni estimation de hauteur.

2. Interfaces de chat IA

Streaming token par token :

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);

  // Met à jour la position du scroller virtuel sans toucher au DOM
  scroller.updateItemHeight(messageId, height + padding);
});
Enter fullscreen mode Exit fullscreen mode

3. Grilles de données (colonnes texte auto-dimensionnées)

function computeColumnWidth(values: string[], font: string, padding: number) {
  let maxWidth = 0;
  for (const value of values) {
    const handle = prepare(value, font);
    // Layout avec largeur infinie = largeur naturelle
    const { height } = layout(handle, Infinity, 20);
    // Utiliser la largeur du segment pour le sizing exact
    maxWidth = Math.max(maxWidth, /* largeur calculée */);
  }
  return maxWidth + padding;
}
Enter fullscreen mode Exit fullscreen mode

4. Flux de contenu multilingue

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' },
];

posts.forEach(post => {
  const handle = prepare(post.text, '16px system-ui');
  const { height } = layout(handle, 400, 24);
});
Enter fullscreen mode Exit fullscreen mode

Tester votre mise en page de texte avec Apidog

Pour des interfaces riches en texte pilotées par API, validez vos données et vos rendus avant de déployer.

Test Apidog

Apidog facilite la simulation de réponses API en streaming et le test de vos intégrations Pretext.js : scénarios multilingues, longueurs variables, cas limites Unicode, etc.

Pour les équipes chat IA, l’environnement Apidog permet de :

  • Simuler des réponses en streaming (texte chunké simulant une sortie LLM)
  • Tester des payloads multilingues
  • Valider les schémas de réponse
  • Automatiser des suites de tests pour les cas limites de rendu texte

Des réponses API de qualité sont indispensables pour un rendu correct, quelle que soit la performance de votre moteur de mesure.

Limitations et critiques connues

Cas limites de précision de rendu

Des écarts peuvent apparaître sur :

  • Polices avec crénage inhabituel
  • Textes avec tailles de police mixtes
  • Différences sous-pixel Canvas vs DOM
  • Particularités de shaping selon navigateur

Pour le scroll virtuel, ces écarts sont négligeables ; pour un rendu typographique pixel-perfect, attention.

La mesure Canvas a un coût

prepare() sollicite Canvas. Si vous créez des milliers de poignées uniques par frame, cela peut devenir un bottleneck. Privilégiez le caching ou le batch.

Pas de support des propriétés CSS avancées

Seule la police est prise en compte. Pas de gestion native de :

  • letter-spacing
  • word-spacing
  • text-indent
  • text-transform
  • font-feature-settings
  • font-variant

Si votre mise en page dépend de ces propriétés, la hauteur calculée pourra diverger de celle du navigateur.

Ce n'est pas un moteur de rendu

Pretext.js mesure, il ne rend pas. Vous avez toujours besoin du DOM ou d'un rendu Canvas/SVG pour afficher le texte.

Pretext.js vs. approches traditionnelles

Caractéristique Pretext.js Mesure DOM Hauteurs estimées
Vitesse (1K éléments) ~2ms ~94ms ~0ms
Précision Élevée (Canvas) Parfaite (DOM) Faible
Dépendance DOM Aucune après prepare() Complète Aucune
Déclencheurs de reflow 0 1 par mesure 0
Multilingue Unicode complet Complet (natif) Faible
Support propriétés CSS Limité Complet Aucun
Surcharge mémoire Segments en cache Nœuds DOM Minimale
Layouts réactifs 1 prepare(), N layout() Re-mesure à chaque largeur Ré-estimation

Pour la précision pixel, le DOM reste la référence. Pour la performance sur des milliers d’éléments, Pretext.js domine.

Démarrer

Installation

npm install @chenglou/pretext
# ou
pnpm add @chenglou/pretext
# ou
bun add @chenglou/pretext
Enter fullscreen mode Exit fullscreen mode

Utilisation de base

import { prepare, layout } from '@chenglou/pretext';

// Mesurer un paragraphe
const handle = prepare(
  'Pretext.js computes text layout without touching the DOM.',
  '16px "Inter"'
);

// Obtenir la hauteur à une largeur donnée
const result = layout(handle, 600, 24);
console.log(result.height);    // ex: 48 (2 lignes x 24px)
console.log(result.lineCount); // ex: 2
Enter fullscreen mode Exit fullscreen mode

Intégration avec 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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Ce pattern permet d’obtenir un chat virtuel avec des hauteurs précises avant tout rendu DOM – plus de sauts, plus de reflow.

Aire de jeux interactive

Testez en temps réel sur pretextjs.dev/playground : collez du texte, ajustez la police et la largeur, observez le layout.

Quand NE PAS utiliser Pretext.js

  • Pages statiques : CSS gère très bien la mise en page pour des contenus fixes.
  • Mise en page d’impression pixel-perfect : Les différences sous-pixel importent, préférez le DOM.
  • Style CSS complexe : Si vos hauteurs dépendent de propriétés CSS avancées, attendez-vous à des divergences.
  • Rendu côté serveur : Pretext.js nécessite Canvas (non disponible nativement en Node.js).
  • Petites listes statiques : Pour 50 items, la mesure DOM est suffisante et ne vaut pas une dépendance supplémentaire.

FAQ

Pretext.js est-il prêt pour la production ?

Lancée en 2026, déjà 14 000+ étoiles et utilisée en production chez Midjourney. Suite de tests solide, mais testez avec vos propres polices et contenus.

Fonctionne-t-il avec React/Vue/Svelte ?

Oui, Pretext.js est framework-agnostique. Utilisez prepare() et layout() où vous voulez.

Comment gère-t-il les polices web ?

prepare() mesure avec la police chargée au moment de l’appel. Si la police web n’est pas prête, les mesures seront fausses. Vérifiez le chargement avec document.fonts.ready.

Utilisable pour Canvas/SVG ?

Oui. Utilisez les hauteurs/sauts calculés pour positionner du texte sur Canvas, SVG, WebGL ou DOM.

Prise en charge des langues RTL ?

Oui, arabe, hébreu, direction mixte, etc.

Poids du bundle ?

15 Ko minifié, zéro dépendance, utilise seulement Canvas et Intl.Segmenter si dispo.

Précision vs mesure DOM ?

1 à 2 pixels d’écart typique. Si vous utilisez letter-spacing ou word-spacing, attendez-vous à plus de différence.

Mesure de texte stylisé (gras, italique, tailles mixtes) ?

Une seule police par prepare(). Pour du texte à styles mixtes, segmentez le texte par style et créez plusieurs poignées.

Conclusion

Pretext.js permet une mesure rapide et précise du texte sans reflow DOM. Idéale pour le virtuel scroll, les chats, et les grilles de données, la bibliothèque remplace des hacks complexes par deux appels de fonction.

Ce n’est pas une solution miracle : pas de support CSS avancé, quelques écarts sous-pixel, pas de support SSR natif. Mais pour le pré-calcul de hauteurs texte dans les listes virtualisées, rien de comparable.

Prêt à accélérer vos interfaces textuelles ? Commencez par tester vos endpoints API avec Apidog pour garantir la solidité de vos données, puis intégrez Pretext.js dans votre pipeline de rendu.

Top comments (0)