A component for truncating and revealing content with smooth motion and fade effects.
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus quisquam assumenda eligendi provident magni. error voluptatibus obcaecati ab qui necessitatibus.
<script setup lang="ts">
import { ref } from 'vue'
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable'
import { ShowMore, ShowMoreItem, ShowMoreButton, ShowMoreContent } from '@/components/ui/show-more'
import { useBreakpoints, breakpointsTailwind } from '@vueuse/core'
import { ChevronDown, ChevronUp } from 'lucide-vue-next'
// This is only for demonstration purposes to show the text on mobile and adjust the size to show or hide it.
const breakpoints = useBreakpoints(breakpointsTailwind)
const isMobile = breakpoints.smaller('sm')
// This is only for demonstration purposes to show the text on mobile and adjust the size to show or hide it.
const openItem = ref('none')
const shortText = {
preview: 'Lorem ipsum dolor sit, amet consectetur adipisicing elit.',
expanded:
'Minus quisquam assumenda eligendi provident magni. error voluptatibus obcaecati ab qui necessitatibus.',
}
</script>
<template>
<ResizablePanelGroup direction="horizontal" class="rounded-lg border">
<!-- LEFT PANEL (CONTENT) -->
<ResizablePanel :default-size="60">
<!-- ShowMore Demo Content -->
<div class="size-full">
<ShowMore
:threshold="3"
type="single"
collapsible
v-model="openItem"
fade
:animation="{
duration: 0.5,
ease: 'backOut',
}"
>
<ShowMoreItem value="demo" v-slot="{ isTruncated }">
<div class="p-6">
<ShowMoreContent class="text-muted-foreground">
<!-- This is only for demonstration purposes to show the text on mobile and adjust the size to show or hide it. -->
<p v-if="isMobile">{{ shortText.preview }}</p>
<p v-else>{{ shortText.preview }} {{ shortText.expanded }}</p>
</ShowMoreContent>
<div v-if="isTruncated" class="flex items-center gap-4 mt-4">
<div class="flex-1 h-px bg-border"></div>
<ShowMoreButton
class="group h-8 px-4 text-xs font-semibold uppercase tracking-wide rounded-full border border-border flex items-center gap-2 [&>svg]:hidden"
>
<span v-if="openItem !== 'demo'" class="flex items-center gap-2">
Show More
<ChevronDown class="w-3 h-3" />
</span>
<span v-else class="flex items-center gap-2">
Show Less
<ChevronUp class="w-3 h-3" />
</span>
</ShowMoreButton>
<div class="flex-1 h-px bg-border"></div>
</div>
</div>
</ShowMoreItem>
</ShowMore>
</div>
</ResizablePanel>
<!-- HANDLE -->
<ResizableHandle />
<!-- RIGHT PANEL -->
<ResizablePanel :default-size="40">
<div class="h-full flex items-center justify-center text-xs text-muted-foreground">
horizontal Resize
</div>
</ResizablePanel>
</ResizablePanelGroup>
</template>The ShowMore component allows you to keep your interface clean by truncating long content and giving users the choice to expand it. It features height-based animations via motion-v and an optional fade effect for a premium feel.
Installation
pnpm dlx sulaf@latest add show-more
Usage
Basic Usage
Wrap your content in ShowMoreContent and use ShowMoreButton to toggle the state.
<script setup lang="ts">
import { ShowMore, ShowMoreItem, ShowMoreButton, ShowMoreContent } from '@/components/ui/show-more'
</script>
<template>
<ShowMore :threshold="3">
<ShowMoreItem value="item-1">
<ShowMoreContent>
<!-- Your long content here -->
</ShowMoreContent>
<ShowMoreButton>Show More</ShowMoreButton>
</ShowMoreItem>
</ShowMore>
</template>Fade Effect
Add the fade prop to the root component to apply a gradient mask to the bottom of the collapsed content.
<ShowMore fade :threshold="3">
<!-- ... -->
</ShowMore>Custom Animation
You can customize the expansion duration and easing function.
<ShowMore
:animation="{
duration: 0.5,
ease: 'backOut'
}"
>
<!-- ... -->
</ShowMore>ShowMore vs. Accordion
While both components handle content expansion, they serve different architectural purposes:
- Accordion: A structural component where you manually define which part is the trigger (always visible) and which part is the content (collapsed). It's best for FAQ-style lists or structured menus.
- ShowMore: A high-level abstraction built on top of Accordion that handles automatic truncation. You provide the full content, and the component calculates how much to reveal based on a
threshold(lines or characters). It also supports built-in fade-out masks and conditional toggles.
API Reference
ShowMore (Root)
The root component extends Reka UI's AccordionRoot and defines the core truncation logic.
| Prop | Type | Default | Description |
|---|---|---|---|
threshold | number | 3 | The number of lines or characters to show before truncating. |
truncationType | 'lines' | 'chars' | 'lines' | The method used to determine where to truncate. |
fade | boolean | false | Whether to apply a fade-out effect when collapsed. |
forceMount | boolean | true | Keeps content in the DOM for precise height measurements. |
showToggle | boolean | true | Whether to display the toggle button functionality. |
v-model:open | boolean | false | Reactive boolean state for two-way expansion control. |
lineHeight | string | '1.5rem' | CSS line-height used for height calculations. |
animation | ShowMoreAnimation | See below | Motion configuration for expansion. |
ShowMoreItem
A simple wrapper that provides context for a single content block. Extends AccordionItem.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | Required. A unique identifier for the item. |
ShowMoreButton
The trigger component. It automatically handles the aria-expanded state. Extends AccordionTrigger.
ShowMoreContent
The animated container that performs the truncation. Extends AccordionContent.
ShowMoreAnimation
type ShowMoreAnimation = {
duration?: number // Duration in seconds
ease?: Easing // Easing function (e.g., 'easeInOut', 'backOut')
}Last updated on April 14, 2026