Expandable Cards Interactive cards that expand to reveal detailed information with smooth animations. Available in multiple variants including product, weather, and meeting cards.
A product card that expands to reveal detailed features, specifications, and purchase options with smooth animations.
Preview Code
npx mvpblocks add expandable-product-card Open in
npm pnpm yarn bun
npm install motion/react lucide-react react pnpm install motion/react lucide-react react yarn add motion/react lucide-react react bun add motion/react lucide-react react 'use client'; import { Battery, Bluetooth, Fingerprint, Mic, ShoppingCart, Star, } from 'lucide-react'; import { AnimatePresence, motion } from 'framer-motion'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Expandable, ExpandableCard, ExpandableCardContent, ExpandableCardFooter, ExpandableCardHeader, ExpandableContent, ExpandableTrigger, } from '@/components/ui/expandable'; export default function ExpandableProductCard() { return ( <Expandable expandDirection="both" expandBehavior="replace"> {({ isExpanded }) => ( <ExpandableTrigger> <ExpandableCard className="relative w-full" collapsedSize={{ width: 330, height: 220 }} expandedSize={{ width: 500, height: 520 }} hoverToExpand={false} expandDelay={500} collapseDelay={700} > <ExpandableCardHeader> <div className="flex items-center justify-between"> <Badge variant="secondary" className="bg-primary/10 text-primary" > New Arrival </Badge> <Badge variant="outline" className="ml-2"> $129.99 </Badge> </div> </ExpandableCardHeader> <ExpandableCardContent> <div className="mb-4 flex items-start"> <img src="https://pisces.bbystatic.com/image2/BestBuy_US/images/products/6505/6505727_rd.jpg;maxHeight=640;maxWidth=550;format=webp" alt="Product" className="mr-4 rounded-md object-cover" style={{ width: isExpanded ? '120px' : '80px', height: isExpanded ? '120px' : '80px', transition: 'width 0.3s, height 0.3s', }} /> <div className="flex-1"> <h3 className="font-medium tracking-tight text-foreground transition-all duration-300" style={{ fontSize: isExpanded ? '24px' : '18px', fontWeight: isExpanded ? '700' : '400', }} > Sony Headphones </h3> <div className="mt-1 flex items-center"> {[1, 2, 3, 4, 5].map((star) => ( <Star key={star} className="h-4 w-4 fill-current text-primary" /> ))} <AnimatePresence mode="wait"> {isExpanded ? ( <motion.span key="expanded" initial={{ opacity: 0, width: 0 }} animate={{ opacity: 1, width: 'auto' }} exit={{ opacity: 0, width: 0 }} transition={{ duration: 0.2 }} className="ml-2 overflow-hidden text-sm whitespace-nowrap text-muted-foreground" > (128 reviews) </motion.span> ) : ( <motion.span key="collapsed" initial={{ opacity: 0, width: 0 }} animate={{ opacity: 1, width: 'auto' }} exit={{ opacity: 0, width: 0 }} transition={{ duration: 0.2 }} className="ml-2 overflow-hidden text-sm whitespace-nowrap text-muted-foreground" > (128) </motion.span> )} </AnimatePresence> </div> </div> </div> <ExpandableContent preset="fade" keepMounted={false} animateIn={{ initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0 }, transition: { type: 'spring', stiffness: 300, damping: 20 }, }} > <p className="mb-4 max-w-xs text-sm text-muted-foreground"> Experience crystal-clear audio with our latest noise-cancelling technology. Perfect for work, travel, or relaxation. </p> <div className="space-y-4"> {[ { icon: Battery, text: '30-hour battery life', id: 'battery', }, { icon: Bluetooth, text: 'Bluetooth 5.0', id: 'bluetooth' }, { icon: Fingerprint, text: 'Touch controls', id: 'fingerprint', }, { icon: Mic, text: 'Voice assistant compatible', id: 'mic', }, ].map((feature) => ( <div key={feature.id} className="flex items-center text-sm text-muted-foreground" > <feature.icon className="mr-2 h-4 w-4" /> <span>{feature.text}</span> </div> ))} <Button className="w-full"> <ShoppingCart className="mr-2 h-4 w-4" /> Add to Cart </Button> </div> </ExpandableContent> </ExpandableCardContent> <ExpandableContent preset="slide-up"> <ExpandableCardFooter> <div className="flex w-full justify-between text-sm text-muted-foreground"> <span>Free shipping</span> <span>30-day return policy</span> </div> </ExpandableCardFooter> </ExpandableContent> </ExpandableCard> </ExpandableTrigger> )} </Expandable> ); } Expand
import ExpandableProductCard from "@/components/mvpblocks/cards/expandable-product-card" ;
export default function MyComponent () {
return (
< div className = "flex items-center justify-center min-h-screen p-8" >
< ExpandableProductCard />
</ div >
);
}
🎯 Interactive Card : Click to expand and collapse with smooth animations
🎨 Fade Animations : Smooth fade effects for content reveal
⭐ Rating Display : Dynamic star rating with review count
🛍️ Product Features : Displays key product features with icons
📱 Responsive : Adapts beautifully to different screen sizes
🌙 Dark Mode : Seamlessly works with light and dark themes
⚡ Smooth Animations : Powered by motion/react for fluid transitions
🖼️ Dynamic Image : Image resizes smoothly on expand/collapse
The component uses the Expandable UI component which provides:
Presets : fade, slide-up for different animation effects
Delays : Custom expand/collapse delays (500ms expand, 700ms collapse)
Sizes : Collapsed (330x220) and expanded (500x520) sizes
Spring Animations : Custom spring animations for smooth transitions
A weather card that expands to show detailed weather information and 5-day forecast with smooth blur animations.
Preview Code
npx mvpblocks add expandable-weather-card Open in
'use client'; import { Cloud, Droplets, Sun, Wind } from 'lucide-react'; import { Badge } from '@/components/ui/badge'; import { Expandable, ExpandableCard, ExpandableCardContent, ExpandableCardFooter, ExpandableCardHeader, ExpandableContent, ExpandableTrigger, } from '@/components/ui/expandable'; export default function ExpandableWeatherCard() { return ( <Expandable expandDirection="both" expandBehavior="replace"> <ExpandableTrigger> <ExpandableCard collapsedSize={{ width: 300, height: 220 }} expandedSize={{ width: 500, height: 420 }} hoverToExpand={false} expandDelay={100} collapseDelay={400} > <ExpandableCardHeader> <div className="flex items-center justify-between"> <div className="flex items-center"> <Sun className="mr-2 h-8 w-8 text-primary" /> <ExpandableContent preset="blur-sm" keepMounted={true}> <h3 className="text-lg font-medium">Today's Weather</h3> <Badge className="bg-primary/10 text-primary"> 72°F </Badge> </ExpandableContent> </div> </div> </ExpandableCardHeader> <ExpandableCardContent> <div className="mb-4 flex items-center justify-between"> <div> <p className="text-2xl font-bold">72°F</p> <p className="text-sm text-muted-foreground"> Feels like 75°F </p> </div> <div className="text-right"> <p className="font-medium">Sunny</p> <ExpandableContent preset="blur-sm" stagger staggerChildren={0.1} keepMounted={true} animateIn={{ initial: { opacity: 0, y: 20, rotate: -5 }, animate: { opacity: 1, y: 0, rotate: 0 }, transition: { type: 'spring', stiffness: 300, damping: 20 }, }} > <p className="text-sm text-muted-foreground"> High 78° / Low 65° </p> </ExpandableContent> </div> </div> <ExpandableContent preset="blur-sm" stagger staggerChildren={0.1} keepMounted={true} animateIn={{ initial: { opacity: 0, y: 20, rotate: -5 }, animate: { opacity: 1, y: 0, rotate: 0 }, transition: { type: 'spring', stiffness: 300, damping: 20 }, }} > <div className="mb-4 space-y-2"> <div className="flex items-center justify-between"> <div className="flex items-center"> <Cloud className="mr-2 h-5 w-5 text-muted-foreground" /> <span>Humidity</span> </div> <span>45%</span> </div> <div className="flex items-center justify-between"> <div className="flex items-center"> <Wind className="mr-2 h-5 w-5 text-muted-foreground" /> <span>Wind</span> </div> <span>8 mph</span> </div> <div className="flex items-center justify-between"> <div className="flex items-center"> <Droplets className="mr-2 h-5 w-5 text-muted-foreground" /> <span>Precipitation</span> </div> <span>0%</span> </div> </div> <div className="space-y-2"> <h4 className="font-medium">5-Day Forecast</h4> {[ { day: 'Mon', temp: 70 }, { day: 'Tue', temp: 71 }, { day: 'Wed', temp: 72 }, { day: 'Thu', temp: 73 }, { day: 'Fri', temp: 74 }, ].map((forecast) => ( <div key={forecast.day} className="flex items-center justify-between" > <span>{forecast.day}</span> <div className="flex items-center"> <Sun className="mr-2 h-4 w-4 text-primary" /> <span>{forecast.temp}°F</span> </div> </div> ))} </div> </ExpandableContent> </ExpandableCardContent> <ExpandableCardFooter> <p className="text-xs text-muted-foreground"> Last updated: 5 minutes ago </p> </ExpandableCardFooter> </ExpandableCard> </ExpandableTrigger> </Expandable> ); } Expand
import ExpandableWeatherCard from "@/components/mvpblocks/cards/expandable-weather-card" ;
export default function MyComponent () {
return (
< div className = "flex items-center justify-center min-h-screen p-8" >
< ExpandableWeatherCard />
</ div >
);
}
🌤️ Weather Details : Shows temperature, humidity, wind, and precipitation
📅 5-Day Forecast : Displays upcoming weather forecast
🎨 Blur Animations : Smooth blur effects for content reveal
🔄 Staggered Animations : Staggered reveal of weather details
An interactive meeting card that expands to show attendees, location, and meeting details with smooth blur animations.
Preview Code
npx mvpblocks add expandable-meeting-card Open in
'use client'; import { Calendar, Clock, MapPin, MessageSquare, Users, Video, } from 'lucide-react'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Expandable, ExpandableCard, ExpandableCardContent, ExpandableCardFooter, ExpandableCardHeader, ExpandableContent, ExpandableTrigger, } from '@/components/ui/expandable'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip'; export default function ExpandableMeetingCard() { return ( <Expandable expandDirection="both" expandBehavior="replace" initialDelay={0.2} > {({ isExpanded }) => ( <ExpandableTrigger> <ExpandableCard className="relative w-full" collapsedSize={{ width: 320, height: 240 }} expandedSize={{ width: 420, height: 480 }} hoverToExpand={false} expandDelay={200} collapseDelay={500} > <ExpandableCardHeader> <div className="flex w-full items-start justify-between"> <div className="flex flex-col items-start"> <Badge variant="destructive" className="mb-2"> In 15 mins </Badge> <h3 className="text-xl font-semibold text-foreground"> Design Sync </h3> </div> <TooltipProvider> <Tooltip> <TooltipTrigger asChild> <Button size="icon" variant="outline" className="h-8 w-8"> <Calendar className="h-4 w-4" /> </Button> </TooltipTrigger> <TooltipContent> <p>Add to Calendar</p> </TooltipContent> </Tooltip> </TooltipProvider> </div> </ExpandableCardHeader> <ExpandableCardContent> <div className="mb-4 flex flex-col items-start justify-between"> <div className="flex items-center text-sm text-muted-foreground"> <Clock className="mr-1 h-4 w-4" /> <span>1:30PM → 2:30PM</span> </div> <ExpandableContent preset="blur-md"> <div className="flex items-center text-sm text-muted-foreground"> <MapPin className="mr-1 h-4 w-4" /> <span>Conference Room A</span> </div> </ExpandableContent> </div> <ExpandableContent preset="blur-md" stagger staggerChildren={0.2}> <p className="mb-4 text-sm text-muted-foreground"> Weekly design sync to discuss ongoing projects, share updates, and address any design-related challenges. </p> <div className="mb-4"> <h4 className="mb-2 flex items-center text-sm font-medium text-foreground"> <Users className="mr-2 h-4 w-4" /> Attendees: </h4> <div className="flex -space-x-2 overflow-hidden"> {['Alice', 'Bob', 'Charlie', 'David'].map((name) => ( <TooltipProvider key={name}> <Tooltip> <TooltipTrigger asChild> <Avatar className="ring-1 ring-border"> <AvatarImage src={`/placeholder.svg?height=32&width=32&text=${name[0]}`} alt={name} /> <AvatarFallback>{name[0]}</AvatarFallback> </Avatar> </TooltipTrigger> <TooltipContent> <p>{name}</p> </TooltipContent> </Tooltip> </TooltipProvider> ))} </div> </div> <div className="space-y-2"> <Button variant="destructive" className="w-full"> <Video className="mr-2 h-4 w-4" /> Join Meeting </Button> {isExpanded && ( <Button variant="outline" className="w-full"> <MessageSquare className="mr-2 h-4 w-4" /> Open Chat </Button> )} </div> </ExpandableContent> </ExpandableCardContent> <ExpandableContent preset="slide-up"> <ExpandableCardFooter> <div className="flex w-full items-center justify-between text-sm text-muted-foreground"> <span>Weekly</span> <span>Next: Mon, 10:00 AM</span> </div> </ExpandableCardFooter> </ExpandableContent> </ExpandableCard> </ExpandableTrigger> )} </Expandable> ); } Expand
import ExpandableMeetingCard from "@/components/mvpblocks/cards/expandable-meeting-card" ;
export default function MyComponent () {
return (
< div className = "flex items-center justify-center min-h-screen p-8" >
< ExpandableMeetingCard />
</ div >
);
}
👥 Attendees Display : Shows meeting attendees with avatars
📅 Calendar Integration : Quick add to calendar button
🎨 Blur Animations : Smooth blur effects for content reveal