ClampText
SM · 950B gzip budgetMulti-line text truncation with expand toggle.
- Category
- Media & layout
- Budget
- sm, single-purpose control
- External runtime
- React / React DOM only
axe fixtureSSR rendersource scanbundle budget
Preview
Live fixtureTry the component here. Locale-aware formatting and direction follow the selected language; example content and labels are not automatically translated. Overlay demos use interactive launchers.
Install
npx shadcn@latest add https://gear5-ui.vercel.app/r/clamp-text.jsonCopies the source into your project.
New here? Set up Tailwind and import aliases first →Source & props
Edit on GitHub ↗Prop types and inline documentation are included below. This is repository source; the installer rewrites shared imports for your project.
"use client";
import React, { useCallback, useRef, useState } from "react";
export interface ClampTextProps {
children: React.ReactNode;
lines?: number;
className?: string;
showMoreText?: string;
showLessText?: string;
ariaLabel?: string;
}
export function ClampText({
children,
lines = 3,
className = "",
showMoreText = "Show more",
showLessText = "Show less",
ariaLabel,
}: ClampTextProps) {
const [expanded, setExpanded] = useState(false);
const contentRef = useRef<HTMLDivElement>(null);
const toggle = useCallback(() => {
setExpanded((prev) => !prev);
}, []);
const handleKeyDown = useCallback(
(e: React.KeyboardEvent) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
toggle();
}
},
[toggle]
);
return (
<div className={className} aria-label={ariaLabel}>
<div
ref={contentRef}
style={{
display: "-webkit-box",
WebkitBoxOrient: "vertical",
WebkitLineClamp: expanded ? "unset" : lines,
overflow: expanded ? "visible" : "hidden",
textOverflow: "ellipsis",
}}
>
{children}
</div>
<button
type="button"
onClick={toggle}
onKeyDown={handleKeyDown}
aria-expanded={expanded}
style={{
background: "none",
border: "none",
padding: 0,
cursor: "pointer",
color: "var(--color-primary, #3b82f6)",
fontWeight: 500,
textDecoration: "underline",
textDecorationStyle: "dotted",
textUnderlineOffset: "2px",
marginBlockStart: "0.5rem",
}}
>
{expanded ? showLessText : showMoreText}
</button>
</div>
);
}
What CI checks
Quality contract- Bundled, minified and gzipped against its tier budget
- Audited by axe in the state previewed above
- Rendered through react-dom/server with no browser globals
- Scanned for network calls, dangerous sinks, and unguarded animation