Doug Waltman

ChopLines

ChopLines is a lightweight React component which truncates multiple lines of HTML markup.

Release VersionGzip SizeTest Coverage

I found several React components which truncate plain text, but few that supported truncation of HTML, and none I was happy with. ChopLines solves the challenge by measuring its children and applying truncation for a given height or number of lines.

See it in action

Usage

Add the dependency:

yarn add chop-lines

And then in your component(s):

import ChopLines from 'chop-lines';
PropRequirement
linesrequired if maxHeight is not set
lineHeightrequired if maxHeight is not set
maxHeightrequired if lines & linesHeight are not set
ellipsisOptional (default: "…")
childrenrequired

Implementation Example

import React, { useState } from 'react';
import ChopLines from 'chop-lines';
import insane from 'insane';
import { Wrapper, Ellipsis } from './styles';

const Sanitized = ({ html }) => (
  <div
    dangerouslySetInnerHTML={{
      __html: insane(html, {
        allowedTags: ['p', 'strong', 'em', 'a'],
      }),
    }}
  />
);

const html = `
  <p>
    Lorem ipsum dolor sit amet, consectetur
    adipiscing elit. Duis turpis nunc, feugiat
    nec facilisis ac, pretium non erat.
    <strong>
      Pellentesque habitant morbi tristique
      <a href="#">some link</a>
      senectus et netus et malesuada fames ac
      turpis egestas.
    </strong>
    <em>
      Suspendisse a semper magna. Aenean rhoncus
      eros non quam eleifend, vitae porttitor
      odio bibendum.
    </em>
  </p>
`;

const ChopLinesExample = () => {
  const [isExpanded, setIsExpanded] = useState(false);
  const handleClick = () => setIsExpanded(true);

  return (
    <Wrapper>
      {isExpanded ? (
        <Sanitized html={html} />
      ) : (
        <ChopLines
          lines={3}
          lineHeight={24}
          ellipsis={
            <Ellipsis onClick={handleClick}>
              <span>Read More</span>
            </Ellipsis>
          }
        >
          <Sanitized html={html} />
        </ChopLines>
      )}
    </Wrapper>
  );
}

export default ChopLinesExample;
import styled from 'styled-components';
import { rem, position } from 'styled-tidy';

export const Wrapper = styled.div`
  background: white;
  color: darkslategrey;
  font-size: ${rem(16)};
  font-weight: 400;
  max-width: ${rem(540)};
  padding: ${rem(16)};
  position: relative;

  p a {
    color: dodgerblue;
    text-decoration: underline;
  }
`;

export const Ellipsis = styled.a`
  background: white;
  font-size: ${rem(12)};
  font-weight: 700;
  outline: none;
  text-transform: uppercase;

  span {
    background: lavender;
    border-radius: ${rem(12)};
    color: slateblue;
    display: inline-block;
    padding: 0 ${rem(8)};
  }

  :hover,
  :focus {
    span {
      color: indigo;
    }
  }

  :before {
    ${position('absolute', 0)}
    background: linear-gradient(
      90deg,
      rgba(255,255,255,0),
      white
    );
    content: '';
    width: ${rem(48)};
  }
`;