FluxUI Pro is live - modern UI, powerful animations, zero hassle.
Components
Interactive Calendar

Interactive Calendar

An interactive calendar component with animated date selection and event indicators.

January 2024
S
M
T
W
T
F
S
Events
Today

Installation

1

Install the packages

npm i motion react-icons
2

Add util file

lib/util.ts
import { ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }
3

Copy and paste the following code into your project

interactive-calendar.tsx
"use client"; import { cn } from "@/lib/utils"; import { motion } from "motion/react"; import React, { useEffect, useState } from "react"; import { FiChevronLeft, FiChevronRight, FiCalendar } from "react-icons/fi"; import ComponentContainer from "@/components/features/component-container"; const InteractiveCalendar = () => { const [animationKey, setAnimationKey] = useState(0); useEffect(() => { const interval = setInterval(() => { setAnimationKey((prev) => prev + 1); }, 12000); return () => clearInterval(interval); }); return ( <ComponentContainer className="md:py-20"> <CalendarAnimation key={animationKey} /> </ComponentContainer> ); }; export default InteractiveCalendar; const CalendarAnimation = () => { const [selectedDate, setSelectedDate] = useState(15); const [currentMonth, setCurrentMonth] = useState(0); const months = ["January", "February", "March", "April", "May", "June"]; const days = ["S", "M", "T", "W", "T", "F", "S"]; const dates = Array.from({ length: 31 }, (_, i) => i + 1); const events = [5, 12, 18, 25]; // Days with events useEffect(() => { const interval = setInterval(() => { setSelectedDate((prev) => (prev % 31) + 1); }, 2000); return () => clearInterval(interval); }, []); return ( <div className={cn( "relative", "flex h-[14rem] w-full max-w-[350px] flex-col items-center justify-center", "rounded-md border border-neutral-800 bg-neutral-900 p-6", )} > {/* Header */} <div className="mb-4 flex w-full items-center justify-between"> <motion.button className="flex h-8 w-8 items-center justify-center rounded-full bg-neutral-800 hover:bg-neutral-700" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} onClick={() => setCurrentMonth((prev) => (prev - 1 + months.length) % months.length)} > <FiChevronLeft className="h-4 w-4 text-neutral-400" /> </motion.button> <div className="flex items-center gap-2"> <FiCalendar className="h-4 w-4 text-cyan-400" /> <span className="text-sm font-semibold text-white"> {months[currentMonth]} 2024 </span> </div> <motion.button className="flex h-8 w-8 items-center justify-center rounded-full bg-neutral-800 hover:bg-neutral-700" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} onClick={() => setCurrentMonth((prev) => (prev + 1) % months.length)} > <FiChevronRight className="h-4 w-4 text-neutral-400" /> </motion.button> </div> {/* Days of week */} <div className="mb-2 grid w-full grid-cols-7 gap-1"> {days.map((day, dayIndex) => ( <div key={`day-${dayIndex}`} className="flex h-6 items-center justify-center text-xs font-medium text-neutral-500" > {day} </div> ))} </div> {/* Calendar Grid */} <div className="grid w-full grid-cols-7 gap-1"> {dates.map((date, index) => { const hasEvent = events.includes(date); const isSelected = date === selectedDate; const isToday = date === 15; return ( <motion.button key={`date-${date}`} className={cn( "relative flex h-8 w-8 items-center justify-center rounded-lg text-xs font-medium transition-colors", isSelected ? "bg-cyan-500 text-white shadow-lg shadow-cyan-500/30" : isToday ? "bg-neutral-700 text-cyan-400" : "bg-neutral-800 text-neutral-300 hover:bg-neutral-700" )} initial={{ opacity: 0, scale: 0.8 }} animate={{ opacity: 1, scale: isSelected ? 1.1 : 1, }} transition={{ duration: 0.3, delay: index * 0.02, ease: "easeOut", }} whileHover={{ scale: isSelected ? 1.1 : 1.05 }} whileTap={{ scale: 0.95 }} onClick={() => setSelectedDate(date)} > {date} {/* Event indicator */} {hasEvent && ( <motion.div className="absolute -bottom-1 h-1 w-1 rounded-full bg-orange-400" initial={{ scale: 0 }} animate={{ scale: 1 }} transition={{ duration: 0.3, delay: index * 0.05 }} /> )} {/* Selection glow */} {isSelected && ( <motion.div className="absolute inset-0 rounded-lg bg-cyan-400/20" initial={{ opacity: 0 }} animate={{ opacity: [0, 0.5, 0] }} transition={{ duration: 2, ease: "easeInOut", repeat: Infinity }} /> )} </motion.button> ); })} </div> {/* Legend */} <div className="mt-4 flex items-center justify-center gap-4 text-xs text-neutral-500"> <div key="events-legend" className="flex items-center gap-1"> <div className="h-2 w-2 rounded-full bg-orange-400" /> <span>Events</span> </div> <div key="today-legend" className="flex items-center gap-1"> <div className="h-2 w-2 rounded-full bg-cyan-400" /> <span>Today</span> </div> </div> </div> ); };
4

Update the import paths to match your project setup

Props

PropTypeDefaultDescription