Expandable Cards

Interactive cards that expand to reveal detailed information with smooth animations. Available in multiple variants including product, weather, and meeting cards.

Product Card

A product card that expands to reveal detailed features, specifications, and purchase options with smooth animations.

Installation

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

Copy the Product Card Component

'use client';import {  Battery,  Bluetooth,  Fingerprint,  Mic,  ShoppingCart,  Star,} from 'lucide-react';import { AnimatePresence, motion } from 'motion/react';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-blue-100 text-blue-800"                >                  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-gray-800 transition-all duration-300 dark:text-white"                    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-yellow-400"                      />                    ))}                    <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-gray-600 dark:text-gray-400"                        >                          (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-gray-600 dark:text-gray-400"                        >                          (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-gray-600 dark:text-gray-400">                  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-gray-600 dark:text-gray-400"                    >                      <feature.icon className="mr-2 h-4 w-4" />                      <span>{feature.text}</span>                    </div>                  ))}                  <Button className="w-full bg-blue-600 text-white hover:bg-blue-700">                    <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-gray-600 dark:text-gray-400">                  <span>Free shipping</span>                  <span>30-day return policy</span>                </div>              </ExpandableCardFooter>            </ExpandableContent>          </ExpandableCard>        </ExpandableTrigger>      )}    </Expandable>  );}

Usage

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>
  );
}

Features

  • 🎯 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

Customization

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

Examples

Weather Card

A weather card that expands to show detailed weather information and 5-day forecast with smooth blur animations.

Copy the Weather Card Component

'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-yellow-400" />                <ExpandableContent preset="blur-sm" keepMounted={true}>                  <h3 className="text-lg font-medium">Today&apos;s Weather</h3>                  <Badge                    variant="secondary"                    className="bg-blue-100 text-blue-800"                  >                    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-gray-600 dark:text-gray-400">                  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-gray-600 dark:text-gray-400">                    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-gray-400" />                    <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-gray-400" />                    <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-gray-400" />                    <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-yellow-400" />                      <span>{forecast.temp}°F</span>                    </div>                  </div>                ))}              </div>            </ExpandableContent>          </ExpandableCardContent>          <ExpandableCardFooter>            <p className="text-xs text-gray-500 dark:text-gray-400">              Last updated: 5 minutes ago            </p>          </ExpandableCardFooter>        </ExpandableCard>      </ExpandableTrigger>    </Expandable>  );}

Usage

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>
  );
}

Key Features

  • 🌤️ 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

Meeting Card

An interactive meeting card that expands to show attendees, location, and meeting details with smooth blur animations.

Copy the Meeting Card Component

'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="secondary"                    className="mb-2 bg-red-100 text-red-600 dark:bg-red-900 dark:text-red-100"                  >                    In 15 mins                  </Badge>                  <h3 className="text-xl font-semibold text-gray-800 dark:text-white">                    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-gray-600 dark:text-gray-300">                  <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-gray-600 dark:text-gray-300">                    <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-gray-700 dark:text-gray-200">                  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-gray-800 dark:text-gray-100">                    <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="border-2 border-white dark:border-gray-800">                              <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 className="w-full bg-red-600 text-white hover:bg-red-700">                    <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-gray-600 dark:text-gray-300">                  <span>Weekly</span>                  <span>Next: Mon, 10:00 AM</span>                </div>              </ExpandableCardFooter>            </ExpandableContent>          </ExpandableCard>        </ExpandableTrigger>      )}    </Expandable>  );}

Usage

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>
  );
}

Key Features

  • 👥 Attendees Display: Shows meeting attendees with avatars
  • 📅 Calendar Integration: Quick add to calendar button
  • 🎨 Blur Animations: Smooth blur effects for content reveal