Fitting Text, Anywhere

There are many JavaScript libraries for fitting text to fill a container by finding the ideal font size, but these libraries don’t work well in environments outside the browser, e.g. static site generators, dynamic code that runs on a server, etc.

@altano/satori-fit-text bridges the gap by allowing you to fit text to a container in any environment that vercel/satori supports1.

Demonstration

Hard-coded font size

@altano/satori-fit-text font size

Unfitted TextFitted Text

Installation

1# Using NPM
2npm install @altano/satori-fit-text
3# Using Yarn
4yarn add @altano/satori-fit-text
5# Using PNPM
6pnpm add @altano/satori-fit-text
1# Using NPM
2npm install @altano/satori-fit-text
3# Using Yarn
4yarn add @altano/satori-fit-text
5# Using PNPM
6pnpm add @altano/satori-fit-text

Example: Open Graph Card

If you’re rendering open graph cards in something like Next.js you may want the text of your cards to be dynamic text (e.g. the title of a blog post) but also fill up the entire card. Instead of hard-coding the font size, which isn’t feasible in this case, use @altano/satori-fit-text in opengraph-image.tsx:

1import { findLargestUsableFontSize, type Font } from "@altano/satori-fit-text";
2
3async function getInter(): Promise<Font> {
4 // ...
5}
6
7export default async function Image() {
8 const title = "The homepage of this site.";
9 const font = await getInter();
10 const opengraphDimensions = {
11 width: 1200,
12 height: 630,
13 };
14
15 const titleFontSize = await findLargestUsableFontSize({
16 text: title,
17 font: font,
18 maxWidth: opengraphDimensions.width,
19 maxHeight: opengraphDimensions.height,
20 });
21
22 const card = (
23 <main
24 style={{
25 lineHeight,
26 padding: 0,
27 margin: 0,
28 }}
29 >
30 <h1
31 style={{
32 margin: 0,
33 fontSize: titleFontSize,
34 }}
35 >
36 {title}
37 </h1>
38 </main>
39 );
40
41 return new ImageResponse(card, {
42 ...opengraphDimensions,
43 fonts: [font],
44 });
45}
1import { findLargestUsableFontSize, type Font } from "@altano/satori-fit-text";
2
3async function getInter(): Promise<Font> {
4 // ...
5}
6
7export default async function Image() {
8 const title = "The homepage of this site.";
9 const font = await getInter();
10 const opengraphDimensions = {
11 width: 1200,
12 height: 630,
13 };
14
15 const titleFontSize = await findLargestUsableFontSize({
16 text: title,
17 font: font,
18 maxWidth: opengraphDimensions.width,
19 maxHeight: opengraphDimensions.height,
20 });
21
22 const card = (
23 <main
24 style={{
25 lineHeight,
26 padding: 0,
27 margin: 0,
28 }}
29 >
30 <h1
31 style={{
32 margin: 0,
33 fontSize: titleFontSize,
34 }}
35 >
36 {title}
37 </h1>
38 </main>
39 );
40
41 return new ImageResponse(card, {
42 ...opengraphDimensions,
43 fonts: [font],
44 });
45}

Here is a fully-fleshed out, basic example codesandbox.

You can find a more complicated example here that I use on this website where I have a title and a subtitle and I use multiple font weights.

Example: Node.js CLI App

Check out this codesandbox if you want to see a Node.js CLI app that computes the ideal font size for certain dimensions.

Example: Browser/React Component

There are less roundabout solutions2 if you’re going to only fit text in a browser, but the library does technically work just fine there.

NOTE: The sandbox below will NOT work in Firefox before version 126 (the Intl.Segmenter API was not yet implemented).


Footnotes

  1. vercel/satori can currently “be used in browser, Node.js (>= 16), and Web Workers.”

  2. @altano/textfit doesn’t require intermediate svg files and can more quickly fit text using only the DOM.