React hook to fit text in a div
This is a React hook that iteratively adjusts the font size so that text will fit in a div.
- checks if text is overflowing by using
scrollHeight
andoffsetHeight
from https://stackoverflow.com/a/10017343/101911 - uses binary search; makes a maximum of 5 adjustments with a resolution of 5% font size from 20-100%
The code is also in a github repo: use-fit-text
const useFitText = () => {
const MIN_FONT_SIZE = 20;
const MAX_FONT_SIZE = 100;
const RESOLUTION = 5;
const ref = useRef(null);
const [state, setState] = useState({
fontSize: MAX_FONT_SIZE,
fontSizePrev: MIN_FONT_SIZE,
fontSizeMax: MAX_FONT_SIZE,
fontSizeMin: MIN_FONT_SIZE,
});
const { fontSize, fontSizeMax, fontSizeMin, fontSizePrev } = state;
useEffect(() => {
const isDone = Math.abs(fontSize - fontSizePrev) <= RESOLUTION;
const isOverflow =
!!ref.current &&
(ref.current.scrollHeight > ref.current.offsetHeight ||
ref.current.scrollWidth > ref.current.offsetWidth);
const isAsc = fontSize > fontSizePrev;
// return if the font size has been adjusted "enough" (change within RESOLUTION)
// reduce font size by one increment if it's overflowing
if (isDone) {
if (isOverflow) {
const fontSizeNew =
fontSizePrev < fontSize
? fontSizePrev
: fontSize - (fontSizePrev - fontSize);
setState({
fontSize: fontSizeNew,
fontSizeMax,
fontSizeMin,
fontSizePrev,
});
}
return;
}
// binary search to adjust font size
let delta;
let newMax = fontSizeMax;
let newMin = fontSizeMin;
if (isOverflow) {
delta = isAsc ? fontSizePrev - fontSize : fontSizeMin - fontSize;
newMax = Math.min(fontSizeMax, fontSize);
} else {
delta = isAsc ? fontSizeMax - fontSize : fontSizePrev - fontSize;
newMin = Math.max(fontSizeMin, fontSize);
}
setState({
fontSize: fontSize + delta / 2,
fontSizeMax: newMax,
fontSizeMin: newMin,
fontSizePrev: fontSize,
});
}, [fontSize, fontSizeMax, fontSizeMin, fontSizePrev, ref]);
return { fontSize: `${fontSize}%`, ref };
};
Example usage¶
import React from "react";
import useFitText from "use-fit-text";
const Example = () => {
const { fontSize, ref } = useFitText();
return (
<div ref={ref} style={{ fontSize, height: 40, width: 100 }}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</div>
);
}
Related posts
- How to use ast-grep with GraphQL — posted 2024-09-24
- Next.js App Router (RSC) projects w/ open source code — posted 2024-07-30
- Next.js Relay GraphQL Pokemon example — posted 2024-05-22
- Example Node.js Passport.js SAML app using OneLogin — posted 2024-05-10
- Aphrodite to CSS Modules codemod — posted 2022-12-09
- Simple codemod example with jscodeshift — posted 2021-05-03