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

autofit-text

v0.1.0

Published

Auto-fit text to any container. A React component and hook that scales or stretches text to fill its box.

Readme

autofit-text

Auto-fit text to any container. A small React component and hook that scales — or stretches — text to its box, and keeps it fitted through resizes, content changes, and web font loads.

  • Binary-search fitting with sub-pixel precision, no overflow
  • Per-axis stretch modes for edge-to-edge display type
  • Wrapping, alignment, and min/max bounds
  • Zero dependencies, SSR-safe, restores every style it touches

Install

npm install autofit-text

React 17+ is a peer dependency.

Quick start

The rule is one sentence: you give the box a size, the text fits to it. <AutofitText> is the box — size it directly, or let its default width/height: 100% fill a parent you've already sized.

import { AutofitText } from 'autofit-text';

<AutofitText style={{ width: 320, height: 120 }}>HELLO WORLD</AutofitText>;

// or fill an already-sized parent
<div style={{ width: 600, height: 200 }}>
  <AutofitText mode="fill">HELLO WORLD</AutofitText>
</div>;

The box must have a definite height — an auto-height parent gives the text nothing to fit vertically.

Modes

Stretch is per-axis. A stretched axis always reaches the full box; the other axis keeps fit semantics and its constraints.

| Mode | Width | Height | | --- | --- | --- | | fit (default) | fitted | fitted | | fill-width | stretched | fitted | | fill-height | fitted | stretched | | fill | stretched | stretched |

Options that constrain or align a stretched axis are contradictory, so they're ignored with a console warning: fill-width ignores maxWidth and alignX, fill-height ignores maxHeight and alignY, and fill ignores all sizing and alignment options.

Stretching maps the glyph ink — not the line box — edge-to-edge: the hook neutralizes letter-spacing and leading and applies text-box-trim where supported.

Alignment

alignX ('left' | 'center' | 'right') and alignY ('top' | 'center' | 'bottom') place the fitted text within the box along any axis that isn't stretched. Both default to 'center'.

// photo-credit corner
<AutofitText maxFontSize={28} alignX="right" alignY="bottom">CREDIT</AutofitText>

// full-width lower third, pinned to the bottom
<AutofitText mode="fill-width" maxFontSize={32} alignY="bottom">BREAKING</AutofitText>

In fit mode the hook normally leaves positioning to your CSS; passing either align prop opts that element into hook-controlled positioning.

Wrapping

<AutofitText wrap>BREAK ANYWHERE</AutofitText>
<AutofitText wrap={{ on: 'word' }}>ONLY AT SPACES</AutofitText>
<AutofitText wrap={{ belowWidth: 400, on: 'word' }}>WRAP ONLY WHEN NARROW</AutofitText>
  • false (default) — single line, fitted
  • true — always wrap, breaking anywhere
  • { belowWidth?, belowHeight?, on? } — wrap once the container is at or below a threshold (px); omit both thresholds to always wrap. on: 'word' keeps words whole — an unbreakable word shrinks the font rather than splitting.

Sizing constraints

maxWidth, maxHeight, minFontSize, and maxFontSize accept a number (px) or a string with px, %, em, rem, vw, vh, vmin, or vmax. Percentages resolve against the container's content box; em resolves against the element's font-size before fitting, so bounds stay stable across refits. Unsupported units are ignored rather than misread.

minFontSize is a soft floor: it wins while the floor still fits, but if even the floor overflows, the text shrinks past it rather than spill out of the box.

Component props

| Prop | Type | Default | | --- | --- | --- | | mode | 'fit' \| 'fill' \| 'fill-width' \| 'fill-height' | 'fit' | | alignX | 'left' \| 'center' \| 'right' | 'center' | | alignY | 'top' \| 'center' \| 'bottom' | 'center' | | wrap | boolean \| { belowWidth?, belowHeight?, on? } | false | | maxWidth / maxHeight | number \| string | — | | minFontSize / maxFontSize | number \| string | — | | onFit | (info: AutofitInfo) => void | — | | enabled | boolean | true | | as | keyof JSX.IntrinsicElements | 'div' | | className, style | applied to the container | — |

onFit fires after every fit pass with { fontSize, scaleX, scaleY, fits }. enabled: false suspends fitting, detaches observers, and restores the element's original inline styles. The forwarded ref points at the container element.

Hook

useAutofitText is the no-DOM escape hatch: your markup, zero injected elements.

import { useRef } from 'react';
import { useAutofitText } from 'autofit-text';

function Banner() {
  const textRef = useRef<HTMLSpanElement>(null);
  const { refit } = useAutofitText(textRef, undefined, { mode: 'fill' });

  return (
    <div style={{ width: 600, height: 200 }}>
      <span ref={textRef}>HELLO WORLD</span>
    </div>
  );
}
function useAutofitText(
  textRef: RefObject<HTMLElement | null>,
  parentRef?: RefObject<HTMLElement | null> | null,
  options?: UseAutofitTextOptions, // same options as the component
  deps?: DependencyList,
): { refit: () => void };
  • parentRef defaults to the text element's DOM parent; pass it explicitly to fit against a different ancestor.
  • deps — extra values that should force a refit when they change.
  • refit() schedules a fit on the next animation frame. No-op before mount or while enabled is false.

How it works

  1. The container's content box is measured (padding is never written over), and a binary search finds the largest font-size whose scrollWidth/scrollHeight fits the active constraints.
  2. Stretch modes then pin the text to the content box and apply a non-uniform scale() so the stretched axes touch the edges exactly; fitted axes are aligned per alignX/alignY.
  3. Refits run on container resize (ResizeObserver), content change (MutationObserver), and web font load (document.fonts).

Development

npm test          # unit tests (vitest)
npm run example   # demo app with a live JSX sandbox
npm run build     # bundle (tsup)

License

MIT