Line Clamp With React and Tailwindcss

Tailwind has a plugin for line clamping @tailwindcss/line-clamp that uses pure CSS to truncate text to the specified number of lines. What if you want to show an indicator to read the rest of the truncated text?

We can create a react component that dynamically truncates text and shows a toggle for expanding or hiding text.

import React, { FunctionComponent, useState, useCallback } from 'react';

type TextTruncateProps = {
  text: string;
};

/* Note: The number of lines to truncate to needs to be static
otherwise the css classes won't be generated by tailwind. If you need
a different number of lines, create a new component */
export const TextTruncateThree: FunctionComponent<TextTruncateProps> = ({
  text,
}) => {
  const [shouldTruncate, setShouldTruncate] = useState<boolean>(false);
  const [readMore, setReadMore] = useState<boolean>(false);

  // Measure the element to calculate the number of lines and
  // determine whether to truncate
  const measuredRef = useCallback(
    (node: any) => {
      // Before the component mounts the node ref will be null
      if (node?.parentElement) {
        // Calculate the number of lines based on height
        const elHeight = node.offsetHeight;
        const styles = window.getComputedStyle(node);
        const lineHeight = styles
          .getPropertyValue('line-height')
          .replace('px', '');
        const elLineCount = elHeight / parseInt(lineHeight, 10);

        setShouldTruncate(elLineCount > 3);
      }
    },
    [text]
  );

  const shouldClamp = shouldTruncate && !readMore;

  // Our toggle for expanding or hiding truncated text
  let toggle;
  if (readMore) {
    toggle = (
      <span onClick={() => setReadMore(false)}>
        Show less
      </span>
    )
  } else {
    toggle = (
      <span onClick={() => setReadMore(true)}>
        Read more
      </span>
    );
  }

  return (
    <div>
      <p
        ref={measuredRef}
        className={`${shouldClamp ? 'line-clamp-3' : 'line-clamp-none'}`}
      >
        {text}
      </p>
      {shouldTruncate && toggle}
    </div>
  );
};

See also: