npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@luke86_1893/react-pretext

v0.1.1

Published

React wrapper for @chenglou/pretext — fast text measurement and layout without DOM reflow

Readme

react-pretext

React wrapper for @chenglou/pretext — fast multiline text measurement and layout without DOM reflow.

pretext separates text measurement into two phases:

  • prepare(text, font) — segments and measures text using an off-screen canvas (~expensive, cached)
  • layout(handle, width, lineHeight) — pure arithmetic, sub-millisecond per call

react-pretext wraps these phases behind a hook and a component, adds automatic width tracking via ResizeObserver, reads font properties from getComputedStyle when not provided explicitly, and caches prepare() handles in a global LRU cache.


Installation

npm install react-pretext @chenglou/pretext

Peer dependencies: react >= 17, react-dom >= 17, @chenglou/pretext


Quick start

Hook

import { useTextLayout } from 'react-pretext'

function MyComponent() {
  const { ref, height, lineCount } = useTextLayout('Hello world', {
    width: 320,
    fontSize: 16,
    fontFamily: 'Inter',
  })

  return (
    <div ref={ref as React.RefObject<HTMLDivElement>} style={{ width: 320, fontSize: 16 }}>
      Hello world
      <span>height: {height}px, lines: {lineCount}</span>
    </div>
  )
}

Component

import { PreText } from 'react-pretext'

function MyComponent() {
  return (
    <PreText
      fontSize={16}
      fontFamily="Inter"
      onLayout={({ height, lineCount }) => console.log(height, lineCount)}
    >
      Hello world
    </PreText>
  )
}

API

useTextLayout(text, options?)

function useTextLayout(text: string, options?: UseTextLayoutOptions): TextLayoutResult

Options

| Prop | Type | Default | Description | |---|---|---|---| | width | number | — | Container width in px. If omitted, tracked via ResizeObserver on the returned ref. | | lineHeight | number | fontSize * 1.2 | Line height in px. | | withLines | boolean | false | When true, populates lines via layoutWithLines(). | | fontSize | number | from getComputedStyle | | | fontFamily | string | from getComputedStyle | | | fontWeight | string \| number | from getComputedStyle | | | fontStyle | string | from getComputedStyle | | | cacheSize | number | 500 | Override global LRU max size. See configureCache() for deterministic control. |

Returns

| Field | Type | Description | |---|---|---| | ref | RefObject<HTMLElement> | Attach to the container element. Used for auto-width and font fallback. | | height | number \| null | null until first measurement (always null during SSR). | | lineCount | number \| null | null until first measurement. | | lines | LineInfo[] \| null | Populated only when withLines: true. |


<PreText>

All FontProps fields are available as props, plus width and lineHeight from UseTextLayoutOptions, plus:

| Prop | Type | Default | Description | |---|---|---|---| | children | string | required | The text to measure and render. | | renderLines | boolean | false | Render each line as an absolutely positioned <span>. Client-only — see SSR section. | | onLayout | (info: { height, lineCount, lines? }) => void | — | Called after each measurement update. | | as | keyof JSX.IntrinsicElements | "div" | HTML element to render as. |

All other HTML attributes are forwarded to the wrapper element.


configureCache(options)

configureCache({ maxSize: 1000 })

Sets the global LRU cache capacity. Call once at app startup for deterministic behaviour. The default capacity is 500 entries. Entries are keyed on text + fontString and evicted least-recently-used.


Types

type FontProps = {
  fontSize?: number
  fontFamily?: string
  fontWeight?: string | number
  fontStyle?: string
}

type LineInfo = {
  text: string
  width: number
  x: number
  y: number
}

Auto font detection

When FontProps fields are omitted, react-pretext reads them from getComputedStyle on the element attached to ref. This means you can style the element via CSS and skip passing font props entirely:

<PreText style={{ fontSize: '1rem', fontFamily: 'Inter' }} onLayout={console.log}>
  Hello world
</PreText>

If the element is not yet mounted (edge case on first render), the fallback is "normal normal 16px sans-serif".


SSR / Next.js

Default mode (renderLines={false}) — SSR safe

height and lineCount are null on the server. The component renders its children inside the wrapper element without layout data. No crash, no hydration mismatch. After hydration, the hook activates and onLayout fires with real values.

renderLines={true} — client-only

On the server there are no positioned <span> elements. After hydration, absolutely positioned spans appear. This causes a React hydration mismatch warning.

Next.js App Router — mark as client component:

// components/MyText.tsx
'use client'
import { PreText } from 'react-pretext'

export function MyText({ children }: { children: string }) {
  return <PreText renderLines fontSize={16} fontFamily="Inter">{children}</PreText>
}

Next.js Pages Router — use dynamic import:

import dynamic from 'next/dynamic'

const PreText = dynamic(
  () => import('react-pretext').then((m) => m.PreText),
  { ssr: false },
)

When renderLines={true} and height is not yet known (before the first client-side measurement), the wrapper has height: 0 and spans are not visible. This resolves after the first layout cycle on the client.


Demo

npm run demo

Opens a Vite dev server with five interactive examples:

  1. Basic <PreText> with auto font detection
  2. renderLines with custom line highlight
  3. useTextLayout hook with a manual width slider
  4. Live resize — drag the container edge to see layout update in real time
  5. Cache behaviour — edit text to observe prepare() call counts

License

MIT