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

Ashley Innocent

Ashley Innocent

31 March 2026

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

Apidog pour les entreprises

Déploiement sur site

SSO & RBAC

Conforme SOC 2

Découvrir Apidog Enterprise

En bref

Pretext.js est une bibliothèque TypeScript sans dépendance qui mesure et positionne le texte multiligne via des calculs purement arithmétiques au lieu d'opérations DOM. Elle élimine les reflows synchrones forcés, offre une mesure de texte environ 500 fois plus rapide que getBoundingClientRect(), et prend en charge tous les principaux systèmes d'écriture de la planète. Si vous construisez des défilements virtuels, des interfaces de chat ou des grilles de données, cette bibliothèque résout un problème que les navigateurs ont ignoré pendant 30 ans.

Introduction

Chaque fois que votre JavaScript appelle getBoundingClientRect() ou lit offsetHeight, le navigateur arrête tout. Il vide les changements de style en attente, recalcule la mise en page et force un rendu complet. C'est ce qu'on appelle le reflow synchrone forcé, et c'est l'opération la plus coûteuse qu'un navigateur puisse effectuer.

Multipliez cela par 1 000 bulles de chat dans une liste virtuelle. Ou 10 000 lignes dans une grille de données. Le résultat ? Des images perdues, des saccades et des utilisateurs qui pensent que votre application est en panne.

💡
Les équipes Apidog qui développent des frontends basés sur des API connaissent bien cette douleur ; diffuser des données de réponse dans des interfaces utilisateur dynamiques tout en maintenant la fluidité est une bataille constante lorsque votre moteur de mise en page vous combat à chaque étape.
bouton

Cheng Lou, le développeur derrière react-motion (plus de 21 700 étoiles sur GitHub) et contributeur principal à React et ReasonML chez Meta, a créé Pretext.js pour résoudre ce problème. La bibliothèque a été lancée en mars 2026, a atteint plus de 14 000 étoiles sur GitHub en quelques jours, et a suscité l'un des plus grands débats sur Hacker News de l'année.

Cet article explique ce que fait Pretext.js, comment il fonctionne en coulisses, quand vous devriez l'utiliser et où il est insuffisant. Vous saurez si cette bibliothèque a sa place dans votre stack.

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 entièrement par calcul arithmétique ; pas de getBoundingClientRect(), pas de offsetHeight, pas de reflow, pas de surcharge.

L'idée principale est simple. Au lieu de demander au navigateur "quelle est la hauteur de ce texte ?" (ce qui force le navigateur à le rendre d'abord), Pretext.js calcule la réponse mathématiquement en utilisant les métriques de police de l'API Canvas.

Voici toute la surface de l'API :

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

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

// Étape 2 : Mettre en page à n'importe quelle largeur (arithmétique pure, microsecondes)
const { height, lineCount } = layout(handle, 400, 24);

C'est tout. Deux fonctions. L'une mesure les segments de texte et les met en cache. L'autre effectue des calculs arithmétiques pour déterminer la mise en page. L'appel à prepare() est la seule opération qui touche le navigateur (via measureText() de Canvas). Après cela, layout() est purement mathématique.

Pourquoi cela est important pour les applications gourmandes en API

Si vous développez des applications qui consomment des réponses API en streaming, comme des assistants IA, des tableaux de bord en temps réel ou des éditeurs collaboratifs, vous devez connaître la hauteur du texte entrant avant de le rendre. Sans cela, votre défilement virtuel saccade, votre interface de chat saute et vos utilisateurs le remarquent.

Pretext.js vous donne cette hauteur en microsecondes au lieu de millisecondes. La différence s'accumule rapidement.

Le problème résolu par Pretext.js

Pour comprendre pourquoi cette bibliothèque existe, vous devez comprendre ce qui se passe lorsque JavaScript lit les propriétés de mise en page.

Explication du reflow synchrone forcé

Lorsque vous écrivez ce code :

const elements = document.querySelectorAll('.text-block');
elements.forEach(el => {
  const height = el.getBoundingClientRect().height; // REFLOW!
  // use height for positioning...
});

Chaque appel à getBoundingClientRect() force le navigateur à :

  1. Mettre en pause l'exécution de JavaScript
  2. Vider toutes les modifications de style en attente
  3. Recalculer la mise en page pour l'ensemble du document (ou du sous-arbre)
  4. Renvoyer la valeur calculée

C'est ce qu'on appelle le "layout thrashing". Dans une boucle mesurant 1 000 éléments, le navigateur effectue 1 000 recalculs de mise en page complets. Le coût ? Environ 94 millisecondes, ce qui signifie 6 images perdues à 60 ips.

Le problème du défilement virtuel

Les bibliothèques de défilement virtuel (comme react-window ou tanstack-virtual) ont besoin de connaître la hauteur de chaque élément pour calculer les positions de défilement. Pour les éléments à hauteur fixe, c'est trivial. Pour le contenu textuel à hauteur variable, c'est un cauchemar.

La solution de contournement standard consiste à rendre les éléments hors écran, à les mesurer, puis à les positionner. Cela fonctionne mais va à l'encontre du but du défilement virtuel ; vous rendez des nœuds DOM que vous essayez d'éviter de rendre.

Certaines bibliothèques estiment les hauteurs et les corrigent après le rendu, ce qui provoque des sauts visibles. D'autres obligent les développeurs à spécifier des hauteurs fixes, limitant ce que vous pouvez afficher.

Pretext.js élimine toute cette catégorie de solutions de contournement. Vous calculez la hauteur exacte du texte avant qu'aucun nœud DOM n'existe.

Chiffres réels

Pretext.js a publié les résultats de ses benchmarks sur son site :

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

L'amélioration de la vitesse est plus spectaculaire avec des lots plus petits car la surcharge par appel de la mesure DOM reste constante tandis que le coût arithmétique de Pretext scale linéairement.

Comment Pretext.js fonctionne en coulisses

La bibliothèque opère en trois phases distinctes. Comprendre celles-ci vous aide à optimiser son utilisation.

Phase 1 : Segmentation du texte

Lorsque vous appelez prepare(), Pretext.js normalise d'abord votre texte d'entrée. Il gère les espaces blancs, applique les règles de saut de ligne Unicode (UAX #14) et segmente le texte en unités sécables.

C'est là qu'intervient le support multilingue. Le moteur de segmentation gère correctement :

Phase 2 : Mesure Canvas

Après la segmentation, Pretext.js alimente chaque segment via l'API measureText() de Canvas. C'est le seul appel de navigateur que la bibliothèque effectue, et il est rapide car la mesure de texte Canvas ne déclenche pas de reflow de mise en page.

// Interne : comment Pretext mesure le texte
const ctx = offscreenCanvas.getContext('2d');
ctx.font = '16px "Inter"';
const metrics = ctx.measureText('Hello'); // Pas de reflow !
const width = metrics.width; // Largeur d'avance du glyphe

Les mesures sont mises en cache par segment et par combinaison de polices. Si vous appelez prepare() avec le même texte et la même police deux fois, le deuxième appel réutilise les données mises en cache.

Phase 3 : Mise en page arithmétique pure

La fonction layout() prend les largeurs de segments mises en cache et une largeur de conteneur, puis calcule les sauts de ligne à l'aide d'un algorithme glouton :

  1. Additionne les largeurs de segments jusqu'à ce que le total dépasse la largeur du conteneur
  2. Passe à une nouvelle ligne
  3. Répète jusqu'à ce que tous les segments soient placés
  4. Multiplie le nombre de lignes par la hauteur de ligne pour obtenir la hauteur totale

Pas de DOM. Pas de Canvas. Pure addition et comparaison.

C'est pourquoi layout() est si rapide ; il fait le même calcul que vous feriez sur papier avec une règle et une calculatrice.

Le modèle de poignée réutilisable

L'une des meilleures décisions de conception dans Pretext.js est que prepare() renvoie une poignée réutilisable. Un seul appel prepare() fonctionne sur toutes les largeurs de conteneur :

const handle = prepare(longArticleText, '16px "Inter"');

// Calcule la hauteur pour mobile, tablette et bureau en microsecondes
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 }

Ce modèle est parfait pour les calculs de conception réactive. Vous mesurez une fois et disposez instantanément à n'importe quelle largeur.

Cas d'utilisation pratiques

1. Défilement virtuel avec texte de hauteur variable

C'est le cas d'utilisation principal. Voici comment vous intégreriez Pretext.js avec un défileur virtuel :

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

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

Pas de rendu hors écran. Pas d'estimation de hauteur. Pas de sauts visibles lorsque les éléments défilent à l'écran.

2. Interfaces de chat IA

Les assistants IA diffusent les réponses jeton par jeton. Chaque nouveau jeton peut modifier le nombre de lignes, décalant tout ce qui se trouve en dessous. Avec la mesure DOM traditionnelle, chaque mise à jour de jeton déclenche un reflow.

Avec Pretext.js, vous recalculez la hauteur après l'arrivée de chaque fragment sans toucher le 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);
  
  // Met à jour la position du défileur virtuel sans mesure DOM
  scroller.updateItemHeight(messageId, height + padding);
});

3. Grilles de données avec colonnes de texte

Les applications de type tableur nécessitent un dimensionnement automatique des colonnes. Mesurer des milliers de valeurs de cellule via le DOM est coûteux. Pretext.js le rend rapide :

function computeColumnWidth(values: string[], font: string, padding: number) {
  let maxWidth = 0;
  for (const value of values) {
    const handle = prepare(value, font);
    // Disposition avec largeur infinie = une seule ligne = largeur naturelle du texte
    const { height } = layout(handle, Infinity, 20);
    // Utilise le suivi de largeur interne de la poignée pour le dimensionnement des colonnes
    maxWidth = Math.max(maxWidth, /* largeur calculée */);
  }
  return maxWidth + padding;
}

4. Flux de contenu multilingues

Les fils d'actualité des médias sociaux avec du contenu de scripts mixtes (publications chinoises suivies de réponses arabes suivies de commentaires coréens) sont notoirement difficiles à virtualiser car chaque script a des règles de saut de ligne différentes.

Pretext.js les gère tous avec la même 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' },
];

// Même API, résultats corrects pour chaque script
posts.forEach(post => {
  const handle = prepare(post.text, '16px system-ui');
  const { height } = layout(handle, 400, 24);
});

Tester votre mise en page de texte avec Apidog

Lorsque vous construisez des interfaces utilisateur riches en texte et alimentées par des API, obtenir la bonne mise en page n'est que la moitié de la bataille. Vous devez également vérifier que les réponses API qui alimentent vos composants texte fournissent les bonnes données, dans le bon format et à la bonne vitesse.

Apidog rend cela simple. Vous pouvez simuler des réponses API en streaming pour tester comment votre intégration Pretext.js gère le chargement progressif du texte. Configurez des scénarios de test avec différentes longueurs de texte, langues et cas limites Unicode, puis vérifiez que votre défileur virtuel se comporte correctement avant le déploiement.

Pour les équipes qui développent des produits de chat IA, l'environnement de test API d'Apidog vous permet de :

Cela est important car une bibliothèque de mise en page de texte n'est aussi bonne que les données qui y sont introduites. Des réponses API de mauvaise qualité produisent des mises en page de mauvaise qualité, quelle que soit la vitesse de votre moteur de mesure.

bouton

Limitations et critiques connues

Pretext.js n'est pas parfait. Le fil de discussion Hacker News a soulevé plusieurs préoccupations valides qu'il est utile de connaître avant de l'adopter.

Cas limites de précision de rendu

Plusieurs utilisateurs ont signalé que le texte dépassait les cadres dans les démonstrations Safari et Chrome. Les calculs arithmétiques de la bibliothèque peuvent diverger de la mise en page native du navigateur dans des scénarios spécifiques :

Ces cas limites importent moins pour le défilement virtuel (où quelques pixels d'erreur sont invisibles) et davantage pour la composition typographique au pixel près.

La mesure Canvas n'est pas gratuite

L'appel prepare() sollicite toujours le moteur de texte Canvas du navigateur. Pour les applications qui créent des milliers de poignées prepare() uniques par image, cela peut devenir un goulot d'étranglement. La solution est la mise en cache et le traitement par lots, mais la bibliothèque n'impose ni l'un ni l'autre.

Aucune prise en charge des propriétés CSS

Pretext.js mesure le texte brut avec une spécification de police. Il ne tient pas compte des propriétés CSS qui affectent la mise en page :

Si votre style de texte repose sur ces propriétés CSS, la hauteur calculée ne correspondra pas à ce que le navigateur rend. Vous devrez en tenir compte manuellement ou accepter la divergence.

Il ne remplace pas le rendu DOM

Pretext.js vous indique la hauteur du texte. Il ne rend pas le texte pour vous. Vous avez toujours besoin de nœuds DOM (ou d'un rendu Canvas/SVG) pour afficher le texte. La valeur de la bibliothèque réside dans la phase de mesure, pas dans la phase de rendu.

Pretext.js vs. les approches traditionnelles

Caractéristique Pretext.js Mesure DOM Hauteurs estimées
Vitesse (1K éléments) ~2ms ~94ms ~0ms (pas de mesure)
Précision Élevée (basée sur Canvas) Parfaite (vérité absolue) Faible (heuristique)
Dépendance DOM Aucune après prepare() Complète Aucune
Déclencheurs de reflow Zéro Un par mesure Zéro
Multilingue Prise en charge complète d'Unicode Complète (native au navigateur) Faible (ratios codés en dur)
Prise en charge des propriétés CSS Limitée (police uniquement) Complète Aucune
Surcharge mémoire Segments mis en cache Nœuds DOM Minimale
Mises en page réactives Un prepare(), plusieurs layout() Re-mesure par largeur Ré-estimation par largeur

Le bon choix dépend de vos contraintes. Si vous avez besoin d'une précision au pixel près et d'une prise en charge des propriétés CSS, la mesure DOM reste la vérité absolue. Si vous avez besoin de vitesse sur des milliers d'éléments et que vous pouvez tolérer de légères différences sous-pixel, Pretext.js l'emporte haut la main.

Démarrer

Installation

npm install @chenglou/pretext
# ou
pnpm add @chenglou/pretext
# ou
bun add @chenglou/pretext

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 de conteneur spécifique
const result = layout(handle, 600, 24);
console.log(result.height);    // par ex., 48 (2 lignes x 24px)
console.log(result.lineCount); // par ex., 2

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; // rembourrage
    });
  }, [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>
  );
}

Cela vous donne un chat virtuel avec des hauteurs d'éléments précises calculées avant que tout message ne soit rendu dans le DOM. Pas d'estimation, pas de sauts de correction, pas de reflow.

Aire de jeux interactive

Le site web de Pretext.js inclut une aire de jeux interactive à l'adresse pretextjs.dev/playground où vous pouvez coller du texte, choisir des polices, ajuster la largeur du conteneur et voir le calcul de la mise en page en temps réel. C'est le moyen le plus rapide de vérifier le comportement avant l'intégration.

Quand vous ne devriez PAS utiliser Pretext.js

Pretext.js n'est pas l'outil idéal pour tous les problèmes de mesure de texte :

FAQ

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

La bibliothèque a été lancée en mars 2026 et a gagné plus de 14 000 étoiles sur GitHub en quelques jours. Cheng Lou, le créateur, dirige le frontend de Midjourney ; un système de production servant des millions d'utilisateurs. La suite de tests de la bibliothèque couvre des dizaines de langues et de cas limites. Cela dit, c'est une nouvelle version. Verrouillez votre version et testez-la avec vos polices et votre contenu spécifiques.

Pretext.js fonctionne-t-il avec React, Vue et Svelte ?

Oui. Pretext.js est indépendant du framework. C'est une bibliothèque TypeScript pure avec deux fonctions. Vous appelez prepare() et layout() partout où vous avez besoin de mesures de texte ; dans les hooks React, les composables Vue, les stores Svelte ou le JavaScript pur.

Comment Pretext.js gère-t-il les polices web ?

La fonction prepare() mesure le texte en utilisant la police que le navigateur a chargée au moment de l'appel. Si votre police web n'a pas encore été chargée, la mesure utilisera la police de remplacement et produira des résultats incorrects. Assurez-vous que vos polices sont chargées avant d'appeler prepare(). Utilisez l'API Font Loading (document.fonts.ready) pour vérifier.

Puis-je utiliser Pretext.js pour le rendu Canvas ou SVG ?

Oui. La bibliothèque calcule la mise en page du texte de manière agnostique vis-à-vis de la cible de rendu. Vous pouvez utiliser les hauteurs et les sauts de ligne calculés pour positionner le texte dans Canvas 2D, WebGL, SVG ou le DOM. Le site web de Pretext.js montre des exemples de toutes ces cibles de rendu.

Prend-il en charge les langues RTL (de droite à gauche) ?

Oui. Pretext.js gère l'arabe, l'hébreu et d'autres scripts RTL avec une prise en charge bidirectionnelle correcte du texte. Il gère également correctement le texte de direction mixte (par exemple, du texte arabe avec des mots anglais intégrés).

Quelle est la taille du bundle ?

15 Ko minifié sans dépendances. Aucun polyfill n'est requis. La bibliothèque utilise uniquement les API de navigateur standard (Canvas measureText() et Intl.Segmenter lorsqu'elles sont disponibles).

Quelle est sa précision par rapport à la mesure DOM ?

Pour la plupart des contenus textuels, Pretext.js correspond à la mise en page DOM à 1 ou 2 pixels près. La précision dépend de la police et des propriétés CSS que vous utilisez. Les propriétés comme letter-spacing et word-spacing ne sont pas prises en compte, donc si vous les utilisez, attendez-vous à des différences plus importantes. Pour le défilement virtuel, où quelques pixels d'erreur sont invisibles, la précision est plus que suffisante.

Pretext.js peut-il mesurer du texte stylisé (gras, italique, tailles mixtes) ?

Chaque appel prepare() prend une seule spécification de police. Pour le texte avec des styles mixtes (mots en gras dans du texte normal), vous devrez segmenter le texte vous-même et créer des poignées distinctes pour chaque série de styles. C'est une limitation connue qui pourrait être abordée dans les futures versions.

Conclusion

Pretext.js résout un problème que les navigateurs ont ignoré pendant trois décennies : la mesure rapide et précise du texte sans reflow DOM. Pour les développeurs qui construisent des défilements virtuels, des interfaces de chat, des grilles de données ou toute interface qui doit mesurer des milliers de blocs de texte, cette bibliothèque remplace toute une catégorie de solutions de contournement par deux appels de fonction.

La bibliothèque n'est pas une solution miracle. Elle ne prend pas en charge les propriétés de texte CSS au-delà de la spécification de police, présente des différences mineures de précision sous-pixel et ne fonctionne pas encore côté serveur. Mais pour son cas d'utilisation cible ; le pré-calcul des hauteurs de texte pour les listes virtualisées ; rien d'autre ne s'en approche.

Prêt à construire des interfaces utilisateur textuelles plus rapides ? Commencez par tester vos points de terminaison API avec Apidog pour vous assurer que votre couche de données est solide, puis intégrez Pretext.js dans votre pipeline de rendu.

bouton

Pratiquez le Design-first d'API dans Apidog

Découvrez une manière plus simple de créer et utiliser des API