Singkatnya
Pretext.js adalah pustaka TypeScript tanpa dependensi yang mengukur dan memposisikan teks multi-baris melalui aritmatika murni alih-alih operasi DOM. Ini menghilangkan reflow sinkron paksa, menghasilkan pengukuran teks ~500x lebih cepat daripada getBoundingClientRect(), dan mendukung setiap sistem penulisan utama di planet ini. Jika Anda membangun scroller virtual, UI obrolan, atau data grid, pustaka ini adalah solusi yang sudah lama dibutuhkan.
Pendahuluan
Setiap kali JavaScript Anda memanggil getBoundingClientRect() atau membaca offsetHeight, browser melakukan reflow sinkron paksa—menghentikan eksekusi, membersihkan perubahan gaya, menghitung ulang tata letak, dan merender ulang. Ini adalah operasi browser yang paling mahal.
Bayangkan Anda melakukan ini untuk 1.000 gelembung obrolan di daftar virtual atau 10.000 baris data grid—hasilnya adalah performa buruk, lag, dan UX yang mengecewakan.
💡 Apidog: Tim yang membangun frontend berbasis API sangat paham tantangan ini; mengalirkan data respons ke UI dinamis tanpa gangguan adalah perjuangan konstan saat mesin tata letak browser tidak berpihak pada Anda.
Cheng Lou, pengembang di balik react-motion dan kontributor utama React/ReasonML di Meta, membangun Pretext.js untuk mengatasi masalah klasik ini. Dirilis Maret 2026 dan langsung viral. Artikel ini akan membahas apa itu Pretext.js, cara kerjanya, kapan menggunakannya, kekurangan, dan implementasi teknis agar Anda tahu apakah library ini cocok untuk stack Anda.
Apa itu Pretext.js?
Pretext.js adalah engine tata letak teks JavaScript/TypeScript murni. Ia mengukur dan memposisikan teks multi-baris sepenuhnya melalui aritmatika—tanpa getBoundingClientRect(), tanpa offsetHeight, tanpa reflow, dan tanpa thrash layout.
Alih-alih bertanya ke browser "seberapa tinggi teks ini?" (yang memicu render), Pretext.js menghitungnya secara matematis menggunakan metrik font dari Canvas API.
API utama:
import { prepare, layout } from '@chenglou/pretext';
// 1. Siapkan teks (sekali, bisa di-cache)
const handle = prepare('Halo, pretext.js', '16px "Inter"');
// 2. Hitung layout pada lebar berapapun (microsecond speed)
const { height, lineCount } = layout(handle, 400, 24);
Cukup dua fungsi: prepare() menyimpan hasil pengukuran, layout() melakukan aritmatika tata letak. prepare() adalah satu-satunya operasi yang menyentuh browser (Canvas measureText()); setelah itu, semua perhitungan murni matematika.
Mengapa Penting untuk Aplikasi Heavy API
Jika aplikasi Anda menerima data streaming dari API—misal asisten AI, dashboard real-time, atau editor kolaboratif—Anda butuh tahu tinggi teks SEBELUM merender DOM. Tanpa ini, virtual scroller Anda akan macet dan UI terasa tidak mulus.
Pretext.js memberi tinggi teks dalam mikrodetik, bukan milidetik. Bedanya sangat signifikan.
Masalah yang Diselesaikan Pretext.js
Penjelasan Reflow Sinkron Paksa
Contoh berikut mengilustrasikan masalah klasik:
const elements = document.querySelectorAll('.text-block');
elements.forEach(el => {
const height = el.getBoundingClientRect().height; // REFLOW!
// gunakan tinggi untuk penempatan selanjutnya...
});
Setiap panggilan getBoundingClientRect():
- Menjeda eksekusi JS,
- Membersihkan gaya tertunda,
- Menghitung ulang layout seluruh dokumen/subtree,
- Mengembalikan hasil.
Loop 1.000 elemen = 1.000 reflow = performa buruk (~94ms, kehilangan banyak frame pada 60fps).
Masalah Virtual Scrolling
Library virtual scroller (misal react-window, tanstack-virtual) butuh tahu tinggi setiap item. Untuk tinggi tetap mudah, tapi untuk teks dinamis tingginya variabel—ini mimpi buruk.
Solusi klasik: render off-screen, ukur, lalu posisikan. Ini mengalahkan tujuan virtualisasi. Estimasi = lompatan posisi. Pretext.js: hitung tinggi tepat SEBELUM DOM dibuat.
Benchmark
| Pendekatan | 1.000 blok teks | 500 blok teks |
|---|---|---|
DOM (getBoundingClientRect) |
~94ms (6 frame drop) | ~47ms |
Pretext.js (layout()) |
~2ms | ~0.09ms |
| Perbedaan kecepatan | ~47x lebih cepat | ~500x lebih cepat |
Perbedaan makin terasa pada batch kecil karena overhead DOM tetap konstan, sedangkan aritmatika Pretext sangat ringan.
Cara Kerja Pretext.js di Balik Layar
Pretext.js beroperasi dalam tiga fase—pahami ini untuk optimasi implementasi Anda.
Fase 1: Segmentasi Teks
prepare() menormalisasi input: membersihkan whitespace, menerapkan aturan pemisah baris Unicode (UAX #14), dan membagi teks ke unit yang bisa di-break.
Dukungan multibahasa: CJK, RTL (Arab/Ibrani), Thailand (kamus segmentasi), Devanagari (ligatur), emoji (ZWJ), dan soft hyphens.
Fase 2: Pengukuran Canvas
Setiap segmen diukur via Canvas measureText() (tanpa reflow):
const ctx = offscreenCanvas.getContext('2d');
ctx.font = '16px "Inter"';
const metrics = ctx.measureText('Halo');
const width = metrics.width;
Segmen dan font di-cache—prepare() dengan input sama akan menggunakan cache.
Fase 3: Tata Letak Aritmatika
layout() menggunakan lebar segmen yang di-cache dan lebar wadah, lalu menghitung wrapping baris via algoritma greedy:
- Jumlahkan lebar segmen hingga melebihi wadah,
- Pindah baris baru,
- Ulangi hingga selesai,
- Hitung total tinggi (jumlah baris x tinggi baris).
Pure aritmatika, tanpa DOM maupun Canvas.
Pola Handle yang Reusable
prepare() mengembalikan handle reusable di semua lebar:
const handle = prepare(longArticleText, '16px "Inter"');
const mobile = layout(handle, 375, 24);
const tablet = layout(handle, 768, 24);
const desktop = layout(handle, 1200, 24);
Cocok untuk desain responsif—ukur sekali, layout berkali-kali.
Kasus Penggunaan Praktis
1. Virtual Scrolling dengan Teks Tinggi Variabel
Integrasi Pretext.js dengan virtual scroller:
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 };
});
}
// 10.000 item, ~4ms
const heights = computeHeights(chatMessages, 600);
Tanpa render off-screen, tanpa estimasi, tanpa lompatan posisi.
2. Antarmuka Obrolan AI
Untuk streaming token demi token—ukur ulang tinggi dengan setiap token tanpa reflow 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);
scroller.updateItemHeight(messageId, height + padding);
});
3. Data Grid dengan Kolom Teks
Otomatisasi hitung lebar kolom:
function computeColumnWidth(values: string[], font: string, padding: number) {
let maxWidth = 0;
for (const value of values) {
const handle = prepare(value, font);
const { height } = layout(handle, Infinity, 20);
// Gunakan width dari handle untuk ukuran kolom (implementasi tergantung API Pretext.js)
maxWidth = Math.max(maxWidth, /* lebar yang dihitung */);
}
return maxWidth + padding;
}
4. Umpan Multibahasa
Support Unicode penuh langsung:
const posts = [
{ text: 'Pustaka ini mengubah segalanya', lang: 'en' },
{ text: 'Teks RTL dengan tata letak dua arah yang benar', lang: 'ar' },
{ text: 'Teks CJK mendapatkan pemisah tingkat karakter yang tepat', lang: 'zh' },
];
posts.forEach(post => {
const handle = prepare(post.text, '16px system-ui');
const { height } = layout(handle, 400, 24);
});
Menguji Tata Letak Teks Anda dengan Apidog
Saat membangun UI padat teks berbasis API, validasi data sama pentingnya dengan layout. Pastikan respons API yang mengalir ke komponen teks Anda formatnya benar dan konsisten.
Apidog memudahkan membuat mock respons API streaming untuk menguji integrasi Pretext.js Anda. Siapkan skenario dengan panjang teks, bahasa, atau edge-case Unicode berbeda, dan pastikan scroller virtual Anda konsisten sebelum produksi.
Untuk tim AI chat, Apidog memungkinkan:
- Mock streaming response (simulasi output LLM)
- Uji payload multilingual (tangkap bug layout sebelum user)
- Validasi skema response (pastikan field teks sesuai format)
- Suite pengujian otomatis (uji edge-case rendering teks)
Data API yang buruk = layout buruk, secepat apapun engine Anda.
Keterbatasan & Kritik
Akurasi Rendering Edge-case
Kasus tertentu (Safari/Chrome demo) menunjukkan teks melampaui bounding-box. Potensi penyimpangan jika:
- Font dengan kerning tidak biasa
- Campuran ukuran font dalam satu blok
- Perbedaan rendering sub-pixel Canvas vs DOM
- Kekhasan shaping text per-browser
Untuk virtual scrolling, selisih kecil ini umumnya tidak masalah. Untuk layout pixel-perfect, perlu hati-hati.
Pengukuran Canvas Tidak Gratis
prepare() tetap memanggil Canvas API. Membuat ribuan handle unik per frame bisa berat. Solusi: cache & batch handle—Pretext.js menyerahkan ini ke implementasi Anda.
Tidak Mendukung Properti CSS
Pretext.js hanya mengukur via spesifikasi font. Tidak menghitung:
letter-spacingword-spacingtext-indenttext-transformfont-feature-settingsfont-variant
Jika Anda memakai properti ini, hasil Pretext.js bisa berbeda dari browser.
Tidak Merender, Hanya Mengukur
Pretext.js hanya memberi tinggi teks. Untuk render, Anda tetap butuh node DOM/Canvas/SVG. Library ini hanya untuk phase pengukuran.
Pretext.js vs Pendekatan Tradisional
| Fitur | Pretext.js | Pengukuran DOM | Perkiraan Tinggi |
|---|---|---|---|
| Kecepatan (1K item) | ~2ms | ~94ms | ~0ms (tanpa ukur) |
| Akurasi | Tinggi (Canvas) | Sempurna (DOM) | Rendah (heuristik) |
| Ketergantungan DOM | Tidak (setelah prepare) | Penuh | Tidak |
| Reflow | 0 | 1 per pengukuran | 0 |
| Dukungan multibahasa | Unicode penuh | Penuh (browser) | Buruk (rasio hardcode) |
| Dukungan properti CSS | Terbatas (font saja) | Penuh | Tidak |
| Overhead memori | Segmen cache | Node DOM | Minimal |
| Tata letak responsif | Satu prepare(), banyak layout()
|
Ukur ulang per lebar | Estimasi ulang |
Pilih sesuai use-case Anda: butuh akurasi pixel-perfect & properti CSS penuh? DOM tetap standar. Perlu kecepatan & bisa toleransi deviasi kecil? Pretext.js jauh lebih efisien.
Memulai
Instalasi
npm install @chenglou/pretext
# atau
pnpm add @chenglou/pretext
# atau
bun add @chenglou/pretext
Penggunaan Dasar
import { prepare, layout } from '@chenglou/pretext';
const handle = prepare(
'Pretext.js menghitung tata letak teks tanpa menyentuh DOM.',
'16px "Inter"'
);
const result = layout(handle, 600, 24);
console.log(result.height); // misal: 48 (2 baris x 24px)
console.log(result.lineCount); // misal: 2
Integrasi dengan 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>
);
}
Dengan ini, Anda dapat membuat virtual chat dengan tinggi item akurat SEBELUM rendering DOM—tanpa estimasi, tanpa jump correction, tanpa reflow.
Arena Bermain Interaktif
Situs Pretext.js menyediakan playground di pretextjs.dev/playground untuk eksperimen layout real-time.
Kapan TIDAK Menggunakan Pretext.js
Pretext.js bukan solusi untuk semua kasus:
- Halaman statis/konten tetap: CSS cukup, tidak butuh library.
- Layout pixel-perfect/cetak: DOM lebih akurat.
-
Heavy CSS styling: Properti seperti
letter-spacingtidak didukung. - Server-side rendering: Canvas API tidak tersedia di Node.js (butuh polyfill).
- Daftar kecil/statis: Untuk <50 item, pengukuran DOM sangat cepat.
FAQ
Apakah Pretext.js siap produksi?
Dirilis Maret 2026, 14.000+ bintang GitHub dalam hitungan hari, digunakan di Midjourney production frontend. Namun tetap, selalu sematkan versi dan uji terhadap font/konten spesifik Anda.
Apakah Pretext.js bekerja di React, Vue, Svelte?
Ya. Pretext.js agnostik framework. Cukup panggil prepare() dan layout() di hooks/composables/stores manapun.
Bagaimana dengan font web?
prepare() mengukur teks dengan font yang sudah dimuat. Jika font web belum ready, hasil salah. Pastikan font sudah loaded (document.fonts.ready) sebelum memanggil prepare().
Untuk rendering Canvas/SVG?
Bisa. Layout yang dihasilkan agnostik target—bisa untuk DOM, Canvas, WebGL, atau SVG.
Mendukung RTL?
Ya. Support Arabic, Hebrew, dan RTL lain, serta teks campuran arah.
Ukuran bundle?
15KB minified, tanpa dependensi, hanya pakai API browser standar.
Akurasi dibanding DOM?
Umumnya deviasi 1-2px dari DOM. Semakin banyak properti CSS, deviasi bertambah. Untuk virtual scrolling, cukup akurat.
Teks bergaya (tebal/miring/ukuran campuran)?
Setiap prepare() hanya untuk satu spesifikasi font. Untuk campuran gaya, pecah sendiri teksnya dan buat handle per gaya.
Kesimpulan
Pretext.js menyelesaikan masalah klasik: pengukuran teks cepat, akurat, tanpa reflow DOM. Untuk virtual scroller, UI chat, data grid, atau interface yang perlu mengukur ribuan blok teks, library ini menggantikan seluruh kategori solusi dengan dua fungsi.
Tidak sempurna—tidak dukung semua properti CSS, deviasi sub-pixel, belum SSR. Namun untuk precompute tinggi teks di virtual list, tidak ada yang menandingi.
Siap membangun UI padat teks dengan performa tinggi? Mulai dengan uji API endpoint Anda menggunakan Apidog untuk memastikan data solid, lalu integrasikan Pretext.js ke rendering pipeline Anda.


Top comments (0)