Skip to content

Show More Only When It's Cut Off

Naseebullah AhmadiSenior Software Engineer, London

`line-clamp` cuts text off, but it can't tell your JavaScript whether it did, so every short description gets a pointless "Show more". `useOverflow` measures the element and answers that one question.

4 min read
#frontend

CSS clamps the text, so only the browser knows whether anything was hidden: compare scrollHeight, the full content, with clientHeight, the box you can see. The answer changes whenever the width does (narrower box, more wrapped lines), so a ResizeObserver measures again on every resize.

@itsnas description.tsx
codedescription.tsx
'use client'
 
function Description({ text }: { text: string }) {
  const ref = useRef<HTMLParagraphElement>(null)
  const [expanded, { toggle }] = useToggle()
  // @src/code/use-overflow
  const overflows = useOverflow(ref, { axis: 'y', paused: expanded })
 
  return (
    <>
      <p className={expanded ? undefined : 'line-clamp-3'} ref={ref}>
        {text}
      </p>
 
      {(overflows || expanded) && (
        <button aria-expanded={expanded} onClick={toggle} type="button">
          {expanded ? 'Show less' : 'Show more'}
        </button>
      )}
    </>
  )
}
main
Nas (@itsnas)

paused is what keeps "Show less" on screen: once expanded, nothing overflows, so a live measurement would hide the very button that collapses it again. Use axis: 'x' for a single truncated line.