Pretext.js: 15KB Bibliothek für 500x schnelleres Textlayout

Ashley Innocent

Ashley Innocent

31 March 2026

Pretext.js: 15KB Bibliothek für 500x schnelleres Textlayout

Apidog für Unternehmen

On-Premises Bereitstellung

SSO & RBAC

SOC 2 konform

Apidog Enterprise entdecken

Kurz gesagt

Pretext.js ist eine TypeScript-Bibliothek ohne Abhängigkeiten, die mehrzeiligen Text durch reine Arithmetik anstatt DOM-Operationen misst und positioniert. Sie eliminiert erzwungene synchrone Reflows, liefert eine ~500x schnellere Textmessung als getBoundingClientRect() und unterstützt jedes wichtige Schriftsystem auf dem Planeten. Wenn Sie virtuelle Scroller, Chat-UIs oder Datengrids erstellen, löst diese Bibliothek ein Problem, das Browser seit 30 Jahren ignoriert haben.

Einführung

Jedes Mal, wenn Ihr JavaScript getBoundingClientRect() aufruft oder offsetHeight liest, stoppt der Browser alles. Er leert ausstehende Stiländerungen, berechnet das Layout neu und erzwingt einen vollständigen Rendering-Durchlauf. Dies wird als erzwungener synchroner Reflow bezeichnet und ist die teuerste Operation, die ein Browser durchführen kann.

Nun multiplizieren Sie das mit 1.000 Chat-Blasen in einer virtuellen Liste. Oder 10.000 Zeilen in einem Datengrid. Das Ergebnis? Verlorene Frames, Ruckeln und Benutzer, die denken, Ihre App sei defekt.

💡
Apidog-Teams, die API-gesteuerte Frontends entwickeln, kennen diesen Schmerz gut; das Streamen von Antwortdaten in dynamische UIs und gleichzeitig alles reibungslos zu halten, ist ein ständiger Kampf, wenn Ihre Layout-Engine Sie auf Schritt und Tritt bekämpft.
Button

Cheng Lou, der Entwickler hinter react-motion (über 21.700 GitHub-Sterne) und ein Kernbeiträger zu React und ReasonML bei Meta, hat Pretext.js entwickelt, um dieses Problem zu beheben. Die Bibliothek wurde im März 2026 veröffentlicht, erreichte innerhalb weniger Tage über 14.000 GitHub-Sterne und löste einen der größten Hacker News-Threads des Jahres aus.

Dieser Artikel erklärt, was Pretext.js tut, wie es unter der Haube funktioniert, wann Sie es verwenden sollten und wo es seine Grenzen hat. Sie werden danach wissen, ob diese Bibliothek in Ihren Tech-Stack gehört.

Was ist Pretext.js?

Pretext.js ist eine reine JavaScript/TypeScript-Textlayout-Engine. Sie misst und positioniert mehrzeiligen Text ausschließlich durch Arithmetik; kein getBoundingClientRect(), kein offsetHeight, kein Reflow, kein Thrashing.

Die Kernidee ist einfach. Anstatt den Browser zu fragen „Wie hoch ist dieser Text?“ (was den Browser dazu zwingt, ihn zuerst zu rendern), berechnet Pretext.js die Antwort mathematisch unter Verwendung von Schriftmetriken aus der Canvas-API.

Hier ist die gesamte API-Oberfläche:

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

Das ist alles. Zwei Funktionen. Eine misst Textsegmente und speichert sie im Cache. Die andere führt arithmetische Operationen zur Layoutberechnung durch. Der prepare()-Aufruf ist die einzige Operation, die den Browser berührt (über Canvas measureText()). Danach ist layout() reine Mathematik.

Warum das für API-intensive Anwendungen wichtig ist

Wenn Sie Anwendungen entwickeln, die Streaming-API-Antworten konsumieren – denken Sie an KI-Assistenten, Echtzeit-Dashboards oder kollaborative Editoren – müssen Sie die Höhe des eingehenden Textes kennen, bevor Sie ihn rendern. Ohne dies ruckelt Ihr virtueller Scroller, Ihre Chat-Oberfläche springt und Ihre Benutzer bemerken es.

Pretext.js liefert Ihnen diese Höhe in Mikrosekunden statt in Millisekunden. Der Unterschied summiert sich schnell.

Das Problem, das Pretext.js löst

Um zu verstehen, warum diese Bibliothek existiert, müssen Sie verstehen, was passiert, wenn JavaScript Layout-Eigenschaften liest.

Erzwungener synchroner Reflow erklärt

Wenn Sie diesen Code schreiben:

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

Jeder getBoundingClientRect()-Aufruf zwingt den Browser dazu:

  1. Die JavaScript-Ausführung zu pausieren
  2. Alle ausstehenden Stiländerungen zu leeren
  3. Das Layout für das gesamte Dokument (oder den Unterbaum) neu zu berechnen
  4. Den berechneten Wert zurückzugeben

Dies wird als „Layout-Thrashing“ bezeichnet. In einer Schleife, die 1.000 Elemente misst, führt der Browser 1.000 vollständige Layout-Neuberechnungen durch. Die Kosten? Ungefähr 94 Millisekunden, was 6 verlorene Frames bei 60 fps bedeutet.

Das Problem des virtuellen Scrollens

Bibliotheken für virtuelles Scrollen (wie react-window oder tanstack-virtual) müssen die Höhe jedes Elements kennen, um Scrollpositionen zu berechnen. Bei Elementen fester Höhe ist dies trivial. Bei Textinhalten variabler Höhe ist es ein Albtraum.

Die Standardlösung besteht darin, Elemente außerhalb des Bildschirms zu rendern, sie zu messen und dann zu positionieren. Dies funktioniert, aber es untergräbt den Zweck des virtuellen Scrollens; Sie rendern DOM-Knoten, die Sie eigentlich vermeiden möchten.

Einige Bibliotheken schätzen Höhen und korrigieren sie nach dem Rendern, was zu sichtbaren Sprüngen führt. Andere zwingen Entwickler, feste Höhen anzugeben, was die Anzeigemöglichkeiten einschränkt.

Pretext.js eliminiert diese gesamte Kategorie von Workarounds. Sie berechnen die exakte Texthöhe, bevor ein DOM-Knoten existiert.

Reale Zahlen

Pretext.js hat Benchmark-Ergebnisse auf ihrer Website veröffentlicht:

Ansatz 1.000 Textblöcke 500 Textblöcke
DOM (getBoundingClientRect) ~94ms (6 verworfene Frames) ~47ms
Pretext.js (layout()) ~2ms ~0,09ms
Geschwindigkeitsunterschied ~47x schneller ~500x schneller

Die Geschwindigkeitsverbesserung ist bei kleineren Batches dramatischer, da der Pro-Aufruf-Overhead der DOM-Messung konstant bleibt, während die arithmetischen Kosten von Pretext linear skalieren.

Wie Pretext.js unter der Haube funktioniert

Die Bibliothek arbeitet in drei verschiedenen Phasen. Das Verständnis dieser hilft Ihnen, die Nutzung zu optimieren.

Phase 1: Textsegmentierung

Wenn Sie prepare() aufrufen, normalisiert Pretext.js zuerst Ihren Eingabetext. Es behandelt Leerzeichen, wendet Unicode-Zeilenumbruchregeln (UAX #14) an und segmentiert den Text in umbrechbare Einheiten.

Hier kommt die mehrsprachige Unterstützung ins Spiel. Die Segmentierungs-Engine verarbeitet korrekt:

Phase 2: Canvas-Messung

Nach der Segmentierung führt Pretext.js jedes Segment durch die Canvas measureText() API. Dies ist der einzige Browser-Aufruf, den die Bibliothek tätigt, und er ist schnell, da die Canvas-Textmessung keinen Layout-Reflow auslöst.

// 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

Die Messungen werden nach Segment- und Schriftkombination zwischengespeichert. Wenn Sie prepare() zweimal mit demselben Text und derselben Schriftart aufrufen, verwendet der zweite Aufruf die zwischengespeicherten Daten wieder.

Phase 3: Reines arithmetisches Layout

Die layout()-Funktion verwendet zwischengespeicherte Segmentbreiten und eine Containerbreite und berechnet dann Zeilenumbrüche mithilfe eines Greedy-Algorithmus:

  1. Summieren Sie die Segmentbreiten, bis die Summe die Containerbreite überschreitet
  2. Brechen Sie in eine neue Zeile um
  3. Wiederholen Sie, bis alle Segmente platziert sind
  4. Multiplizieren Sie die Zeilenanzahl mit der Zeilenhöhe, um die Gesamthöhe zu erhalten

Kein DOM. Keine Canvas. Reine Addition und Vergleich.

Deshalb ist layout() so schnell; es macht dieselbe Mathematik, die Sie auf Papier mit Lineal und Taschenrechner ausführen würden.

Das wiederverwendbare Handle-Muster

Eine der besten Designentscheidungen in Pretext.js ist, dass prepare() ein wiederverwendbares Handle zurückgibt. Ein einziger prepare()-Aufruf funktioniert über alle Containerbreiten hinweg:

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 }

Dieses Muster ist perfekt für Responsive-Design-Berechnungen. Sie messen einmal und legen das Layout bei jeder Breite sofort fest.

Praktische Anwendungsfälle

1. Virtuelles Scrollen mit Text variabler Höhe

Dies ist der primäre Anwendungsfall. So würden Sie Pretext.js in einen virtuellen Scroller integrieren:

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

Kein Off-Screen-Rendering. Keine Höhenschätzung. Keine sichtbaren Sprünge beim Scrollen von Elementen in den sichtbaren Bereich.

2. KI-Chat-Oberflächen

KI-Assistenten streamen Antworten Token für Token. Jedes neue Token kann die Zeilenanzahl ändern und alles darunter verschieben. Bei traditioneller DOM-Messung löst jede Token-Aktualisierung einen Reflow aus.

Mit Pretext.js berechnen Sie die Höhe nach jedem eintreffenden Datenblock neu, ohne das DOM zu berühren:

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. Datengrids mit Textspalten

Tabellenkalkulations-ähnliche Anwendungen benötigen eine automatische Spaltengrößenanpassung. Das Messen von Tausenden von Zellwerten über das DOM ist teuer. Pretext.js macht es schnell:

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. Mehrsprachige Inhalts-Feeds

Social-Media-Feeds mit gemischtsprachigen Inhalten (chinesische Beiträge gefolgt von arabischen Antworten gefolgt von koreanischen Kommentaren) sind notorisch schwer zu virtualisieren, da jede Schriftart unterschiedliche Zeilenumbruchregeln hat.

Pretext.js verarbeitet alle mit derselben 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' },
];

// Same API, correct results for every script
posts.forEach(post => {
  const handle = prepare(post.text, '16px system-ui');
  const { height } = layout(handle, 400, 24);
});

Testen Ihres Textlayouts mit Apidog

Wenn Sie textlastige Benutzeroberflächen erstellen, die von APIs unterstützt werden, ist das richtige Layout nur die halbe Miete. Sie müssen auch überprüfen, ob die API-Antworten, die Ihre Textkomponenten speisen, die richtigen Daten im richtigen Format und mit der richtigen Geschwindigkeit liefern.

Apidog macht dies unkompliziert. Sie können Streaming-API-Antworten simulieren, um zu testen, wie Ihre Pretext.js-Integration das progressive Laden von Text handhabt. Richten Sie Testszenarien mit unterschiedlichen Textlängen, Sprachen und Unicode-Grenzfällen ein und überprüfen Sie dann, ob Ihr virtueller Scroller sich korrekt verhält, bevor Sie bereitstellen.

Für Teams, die KI-Chat-Produkte entwickeln, ermöglicht Ihnen die API-Testumgebung von Apidog:

Dies ist wichtig, denn eine Textlayout-Bibliothek ist nur so gut wie die Daten, die in sie hineinfließen. Schlechte API-Antworten erzeugen schlechte Layouts, unabhängig davon, wie schnell Ihre Mess-Engine läuft.

Button

Bekannte Einschränkungen und Kritikpunkte

Pretext.js ist nicht perfekt. Der Hacker News-Thread hat mehrere gültige Bedenken aufgeworfen, die Sie kennen sollten, bevor Sie es einführen.

Grenzfälle bei der Rendering-Genauigkeit

Mehrere Benutzer berichteten von Text, der in Safari- und Chrome-Demos über die Begrenzungsrahmen hinausragte. Die Arithmetik der Bibliothek kann in bestimmten Szenarien vom nativen Browser-Layout abweichen:

Diese Grenzfälle sind für virtuelles Scrollen weniger relevant (wo einige Pixel Fehler unsichtbar sind) und wichtiger für pixelgenauen Satz.

Canvas-Messung ist nicht kostenlos

Der prepare()-Aufruf trifft immer noch die Canvas-Text-Engine des Browsers. Für Anwendungen, die Tausende einzigartiger prepare()-Handles pro Frame erstellen, kann dies zu einem Engpass werden. Die Lösung liegt im Caching und Batching, aber die Bibliothek erzwingt beides nicht.

Keine Unterstützung für CSS-Eigenschaften

Pretext.js misst reinen Text mit einer Schriftartspezifikation. Es berücksichtigt keine CSS-Eigenschaften, die das Layout beeinflussen:

Wenn Ihr Text-Styling auf diesen CSS-Eigenschaften basiert, stimmt die berechnete Höhe nicht mit dem überein, was der Browser rendert. Sie müssten diese manuell berücksichtigen oder die Diskrepanz akzeptieren.

Es ersetzt nicht das DOM-Rendering

Pretext.js sagt Ihnen, wie hoch Text sein wird. Es rendert den Text nicht für Sie. Sie benötigen weiterhin DOM-Knoten (oder Canvas-/SVG-Rendering), um den Text anzuzeigen. Der Wert der Bibliothek liegt in der Messphase, nicht in der Rendering-Phase.

Pretext.js vs. traditionelle Ansätze

Funktion Pretext.js DOM-Messung Geschätzte Höhen
Geschwindigkeit (1K Elemente) ~2ms ~94ms ~0ms (keine Messung)
Genauigkeit Hoch (Canvas-basiert) Perfekt (Grundwahrheit) Niedrig (heuristisch)
DOM-Abhängigkeit Keine nach prepare() Vollständig Keine
Reflow-Auslöser Null Einer pro Messung Null
Mehrsprachig Volle Unicode-Unterstützung Vollständig (Browser-nativ) Schlecht (fest codierte Verhältnisse)
CSS-Eigenschaftsunterstützung Begrenzt (nur Schriftart) Vollständig Keine
Speicherverbrauch Zwischengespeicherte Segmente DOM-Knoten Minimal
Responsive Layouts Ein prepare(), viele layout() Pro Breite neu messen Pro Breite neu schätzen

Die richtige Wahl hängt von Ihren Einschränkungen ab. Wenn Sie pixelgenaue Genauigkeit und CSS-Eigenschaftsunterstützung benötigen, ist die DOM-Messung immer noch die Grundwahrheit. Wenn Sie Geschwindigkeit über Tausende von Elementen benötigen und geringfügige Subpixel-Unterschiede tolerieren können, gewinnt Pretext.js mit großem Abstand.

Erste Schritte

Installation

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

Grundlegende Verwendung

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);    // e.g., 48 (2 lines x 24px)
console.log(result.lineCount); // e.g., 2

Integration mit 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>
  );
}

Dies ermöglicht Ihnen einen virtuellen Chat mit genauen Elementhöhen, die berechnet werden, bevor Nachrichten im DOM gerendert werden. Keine Schätzung, keine Korrektursprünge, kein Reflow.

Interaktiver Spielplatz

Die Pretext.js-Website enthält einen interaktiven Spielplatz unter pretextjs.dev/playground, wo Sie Text einfügen, Schriftarten auswählen, die Containerbreite anpassen und die Layoutberechnung in Echtzeit sehen können. Dies ist der schnellste Weg, das Verhalten vor der Integration zu überprüfen.

Wann Sie Pretext.js NICHT verwenden sollten

Pretext.js ist nicht das richtige Werkzeug für jedes Textmessproblem:

FAQ

Ist Pretext.js produktionsreif?

Die Bibliothek wurde im März 2026 veröffentlicht und gewann innerhalb weniger Tage über 14.000 GitHub-Sterne. Cheng Lou, der Entwickler, leitet das Frontend von Midjourney; ein Produktionssystem, das Millionen von Benutzern bedient. Die Testsuite der Bibliothek deckt Dutzende von Sprachen und Grenzfälle ab. Dennoch ist es eine neue Version. Fixieren Sie Ihre Version und testen Sie sie mit Ihren spezifischen Schriftarten und Inhalten.

Funktioniert Pretext.js mit React, Vue und Svelte?

Ja. Pretext.js ist Framework-agnostisch. Es ist eine reine TypeScript-Bibliothek mit zwei Funktionen. Sie rufen prepare() und layout() überall dort auf, wo Sie Textmessungen benötigen; innerhalb von React-Hooks, Vue-Composables, Svelte-Stores oder reinem JavaScript.

Wie geht Pretext.js mit Webfonts um?

Die prepare()-Funktion misst Text mit der Schriftart, die der Browser zum Zeitpunkt des Aufrufs geladen hat. Wenn Ihre Webfont noch nicht geladen wurde, verwendet die Messung die Fallback-Schriftart und erzeugt falsche Ergebnisse. Stellen Sie sicher, dass Ihre Schriftarten geladen sind, bevor Sie prepare() aufrufen. Verwenden Sie die Font Loading API (document.fonts.ready) zur Überprüfung.

Kann ich Pretext.js für Canvas- oder SVG-Rendering verwenden?

Ja. Die Bibliothek berechnet Textlayouts, die rendering-zielagnostisch sind. Sie können die berechneten Höhen und Zeilenumbrüche verwenden, um Text in Canvas 2D, WebGL, SVG oder DOM zu positionieren. Die Pretext.js-Website zeigt Beispiele für all diese Rendering-Ziele.

Ja. Pretext.js verarbeitet Arabisch, Hebräisch und andere RTL-Schriften mit korrekter bidirektionaler Textunterstützung. Es verarbeitet auch Texte mit gemischter Richtung (z. B. arabischen Text mit eingebetteten englischen Wörtern) korrekt.

Wie groß ist die Bundle-Größe?

15KB minimiert ohne Abhängigkeiten. Keine Polyfills erforderlich. Die Bibliothek verwendet nur Standard-Browser-APIs (Canvas measureText() und Intl.Segmenter, wo verfügbar).

Wie genau ist es im Vergleich zur DOM-Messung?

Bei den meisten Textinhalten stimmt Pretext.js innerhalb von 1-2 Pixeln mit dem DOM-Layout überein. Die Genauigkeit hängt von der verwendeten Schriftart und den CSS-Eigenschaften ab. Eigenschaften wie letter-spacing und word-spacing werden nicht berücksichtigt. Wenn Sie diese verwenden, erwarten Sie größere Unterschiede. Für virtuelles Scrollen, wo ein paar Pixel Fehler unsichtbar sind, ist die Genauigkeit mehr als ausreichend.

Kann Pretext.js stilisierten Text (fett, kursiv, gemischte Größen) messen?

Jeder prepare()-Aufruf erfordert eine einzelne Schriftartenspezifikation. Für Text mit gemischten Stilen (fett gedruckte Wörter innerhalb von normalem Text) müssten Sie den Text selbst segmentieren und separate Handles für jeden Stilbereich erstellen. Dies ist eine bekannte Einschränkung, die möglicherweise in zukünftigen Versionen behoben wird.

Fazit

Pretext.js löst ein Problem, das Browser drei Jahrzehnte lang ignoriert haben: schnelle, genaue Textmessung ohne DOM-Reflow. Für Entwickler, die virtuelle Scroller, Chat-UIs, Datengrids oder jede andere Schnittstelle erstellen, die Tausende von Textblöcken messen muss, ersetzt diese Bibliothek eine ganze Kategorie von Workarounds durch zwei Funktionsaufrufe.

Die Bibliothek ist keine Universallösung. Sie unterstützt CSS-Texteigenschaften nicht über die Schriftartenspezifikation hinaus, weist geringfügige Subpixel-Genauigkeitsunterschiede auf und funktioniert noch nicht serverseitig. Aber für ihren primären Anwendungsfall – die Vorausberechnung von Texthöhen für virtualisierte Listen – kommt nichts anderes nahe.

Bereit, schnellere, textlastige UIs zu bauen? Beginnen Sie, indem Sie Ihre API-Endpunkte mit Apidog testen, um sicherzustellen, dass Ihre Datenschicht solide ist, und integrieren Sie dann Pretext.js in Ihre Rendering-Pipeline.

Button

Praktizieren Sie API Design-First in Apidog

Entdecken Sie eine einfachere Möglichkeit, APIs zu erstellen und zu nutzen