bigbullui logobigbullui
Componentmedia

Truncate

Single/multi-line clamp with optional expand toggle (MORE/LESS)

Preview

Live Preview
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.

Installation

Tokens guide →

Add to your project via CLI (zero dependencies, code you own):

npx bigbullui add truncate

Or install the full npm package:

npm install bigbullui

Usage

import { Truncate } from "@/components/ui/truncate"

<Truncate lines={2} expandable>...</Truncate>

Source code

truncate.tsx

Zero dependencies (only React + utils). Copy and paste directly into your project:

"use client";
import * as React from "react";
import { cn } from "./lib/utils";

export type TruncateProps = {
  lines?: number;
  expandable?: boolean;
  children: React.ReactNode;
};

const Truncate = ({ lines = 1, expandable = true, children }: TruncateProps) => {
  const containerRef = React.useRef<HTMLDivElement>(null);
  const [showMore, setShowMore] = React.useState(false);

  const toText = (node: React.ReactNode): string => {
    if (typeof node === "string") return node;
    if (typeof node === "number") return String(node);
    return "";
  };

  const displayText = React.useMemo(() => {
    const text = toText(children);
    if (!expandable || text.length <= 200) {
      return text;
    }
    return text.substring(0, 200) + "...";
  }, [children, expandable]);

  if (!expandable) {
    return <div className="whitespace-break-all">{children}</div>;
  }

  const lineHeight = 1.25;
  const maxHeight = lines * lineHeight;

  return (
    <div
      ref={containerRef}
      className={cn(
        "overflow-hidden animate-[fade-in-up_0.3s_ease-out_both]",
        "motion-reduce:animate-none"
      )}
      style={{ maxHeight }}
    >
      {showMore ? children : (
        <div className="flex flex-col">
          {React.Children.toArray(children).slice(0, lines)}
        </div>
      )}
      {showMore ? null : (
        <button
          onClick={() => setShowMore(true)}
          className={cn(
            "mt-1 text-xs font-mono uppercase tracking-[0.15em] text-primary-foreground hover:text-primary",
            "transition-colors"
          )}
        >
          MORE
        </button>
      )}
    </div>
  );
};

export { Truncate };

Props

PropTypeDescription
linesnumberNumber of lines to show before truncation
expandablebooleanWhether to show MORE/LESS toggle