Two interactive demos showing how @chenglou/pretext enables performance optimizations that were previously impossible.
npm install
npm run devThen open http://localhost:5173/ in your browser.
Scroll through 10,000 chat messages with buttery-smooth performance. Every message height is computed by Pretext in pure JavaScript before any DOM node is mounted. Only ~30 DOM nodes exist at any given time, no matter where you scroll.
Try the "Jump to #5000" button — it lands exactly on message 5000. This is impossible with estimated heights because cumulative errors make the scroll position wrong. With Pretext, every message's pixel position is accurate.
Imagine building a chat app. Your most active group has 10,000 messages. What happens when a user opens it?
- Render all 10,000 messages → Phone catches fire, app crashes
- Virtual list with estimated heights → Scroll bar lies, jumps around, "jump to message" lands in wrong place
- Virtual list that measures DOM on first appear → First scroll is jittery, then it's smooth (but slow)
- Virtual list with Pretext pre-computed heights ✓ → First paint is correct, scroll is buttery smooth, jump-to-message works perfectly
- Messages: 10,000 total in the chat
- DOM nodes: Only ~30 mounted at any time (watch this number stay tiny as you scroll)
- Layout time: Typically 30-50ms to compute all 10,000 positions upfront
- Verify button: Compares Pretext predictions vs actual DOM measurements (logs to console)
- Generate 10,000 messages with varying lengths (50% short, 35% medium, 15% long)
- Compute layout once on mount using Pretext to measure every message's height
- Binary search to find which messages intersect the viewport on each scroll frame
- Render only visible + overscan (600px above/below viewport)
- Position with
absoluteat pre-computed pixel coordinates
All layout math happens in JavaScript. The browser just paints pre-positioned elements.
- iMessage-style aesthetic (blue bubbles for "me", gray for "them")
- Sender labels on first message in each group
- Jump to start / message #5000 / end buttons
- Highlight animation when jumping to show accuracy
- Live DOM node counter
Scroll through 10,000 cards with zero DOM measurements. Every card's height is computed by Pretext before render. Only visible cards exist in the DOM (typically 40-60 out of 10,000).
Open your browser's performance profiler and start a recording while scrolling. You'll see virtually no scripting time and no layout work.
- Cards in feed: Toggle between 1,500 / 5,000 / 10,000 cards
- Layout time: Total time to compute all card positions (typically <50ms for 10,000 cards)
- DOM nodes mounted: Only visible cards (~40-60 out of 10,000)
- Total height of feed: Often 400,000+ pixels for large feeds
- Pretext measurements: Equals card count (every card measured once)
- DOM measurements: Always 0 (no
getBoundingClientRect()calls)
- Column controls: Switch between 2 / 3 / 4 column layouts
- Card count controls: Test with 1,500 / 5,000 / 10,000 cards
- Verify accuracy: Click to compare Pretext predictions vs actual DOM measurements
- Custom virtualization: No third-party libraries
- Masonry layout: Shortest-column algorithm with proper card distribution
- LogRocket topic cards: Realistic blog post titles and descriptions
- Generate cards: Create N cards with LogRocket blog post titles and descriptions
- Measure with Pretext: For each card, use
prepare()+layout()to compute text height - Position calculation: Use shortest-column algorithm to determine x/y position
- Virtualization: Only render cards within viewport + overscan area
- Absolute positioning: Use computed x/y for pixel-perfect placement
- Vite + React
- @chenglou/pretext for text measurement
Traditional approaches require rendering text to the DOM to measure it, creating a performance bottleneck:
- Render → Measure → Adjust → Re-render creates layout thrashing
- Estimated heights cause cumulative errors and broken scroll behavior
- Measure-on-demand makes the first scroll jittery
Pretext uses Canvas measureText() plus pure arithmetic to compute dimensions before any DOM operations. This unlocks:
- Virtualized chat at any scale (Demo 1) - 10,000+ messages with perfect scroll
- Masonry feeds without jank (Demo 2) - Compute all positions upfront
- Zero layout thrashing - No forced reflows from
getBoundingClientRect() - 300x faster measurements - Canvas arithmetic vs DOM queries
- Pixel-perfect accuracy - Jump-to-message/card lands exactly right
Perfect for chat apps, social feeds, dashboards, or any UI where text measurement is a bottleneck.