Chat Widget
A floating chat widget with animated open/close, customizable logo, greeting, and actions for booking calls or sending emails.
👉 The chat widget will appear in the bottom-right corner of the page. Look for the blue button in the bottom-right corner.
Installation
- 1
Install Dependencies
npm i framer-motion lucide-react - 2
lib/utils.ts
import { clsx, type ClassValue } from "clsx" import { twMerge } from "tailwind-merge" export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } - 3
Copy the source code
@/components/ui/chat-widget.tsx'use client' import { useState } from 'react' import { motion, AnimatePresence } from 'framer-motion' import { Button } from "@/components/ui/button" import { Card } from "@/components/ui/card" import { ScrollArea } from "@/components/ui/scroll-area" import { ChevronDown, MessageSquare, SendHorizontal, Users } from 'lucide-react' import Image from 'next/image' import Link from 'next/link' import { cn } from '@/lib/utils' interface ChatWidgetProps { className?: string calendarLink: string email: string logo?: string greeting?: string headline?: string } export default function ChatWidget({ className, calendarLink, email, logo = "https://voxlet.vercel.app/logo-transperant-bg.svg", greeting = "Hi there 👋", headline = "How can we help?", }: ChatWidgetProps) { const [isOpen, setIsOpen] = useState(false) return ( <div className={cn("fixed bottom-4 right-4 z-[110] flex flex-col items-end", className)}> <AnimatePresence> {isOpen && ( <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: 20 }} transition={{ duration: 0.3, ease: "easeInOut" }} className="mb-2" > <Card className="bg-background max-w-[calc(100vw-30px)] w-[380px] h-[600px] max-h-[calc(100vh-100px)] flex flex-col rounded-xl overflow-hidden shadow-xl"> <div className="bg-gradient-to-br from-blue-700 via-blue-600 to-blue-400 p-6 pb-[6rem] text-white relative"> <div className='absolute bottom-0 left-0 w-full h-[10rem] bg-gradient-to-t from-background via-background/50 to-transperant pointer-events-none'></div> <div className="flex justify-start items-start"> <Image src={logo} alt={'logo'} height={35} width={35} className="rounded-md z-[111]" /> </div> <div className="mt-16 mb-2 z-[10]"> <h2 className="text-2xl font-semibold">{greeting}</h2> <h1 className="text-3xl font-bold mt-1">{headline}</h1> </div> </div> <ScrollArea className="flex-1 -mt-20"> <div className="p-4 flex flex-col gap-3"> <Link href={calendarLink}> <button className="w-full bg-background hover:bg-muted text-left px-4 py-3 rounded-xl border flex justify-between items-center group transition-colors"> <div> <div className="font-medium">Book a Call</div> <div className="text-sm text-muted-foreground">Let's discuss your needs</div> </div> <Users className="size-5" /> </button> </Link> <a href={`mailto:${email}`}> <button className="w-full text-left px-4 py-3 bg-background hover:bg-muted rounded-lg flex justify-between items-center group transition-colors border" > <div> <div className="font-medium">Send an Email</div> <div className="text-sm text-muted-foreground">{email}</div> </div> <SendHorizontal className="size-5" /> </button> </a> </div> </ScrollArea> </Card> </motion.div> )} </AnimatePresence> <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.8, delay: 0.2 }} > <Button size="icon" className="w-14 h-14 rounded-full bg-blue-600 hover:bg-blue-700 shadow-lg text-white scale-[0.8]" onClick={() => setIsOpen(!isOpen)} > {isOpen ? ( <ChevronDown className="w-8 h-8 text-white scale-[1.5]" /> ) : ( <MessageSquare className="w-8 h-8 fill-current text-white scale-[1.5]" /> )} </Button> </motion.div> </div> ) }
ChatWidgetProps
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | Optional class name applied to the parent container for positioning or styling. |
| calendarLink | string | - | The URL used for the 'Book a Call' button. |
| string | - | The email address used for the 'Send an Email' button. | |
| logo | string | https://voxlet.vercel.app/logo-transperant-bg.svg | An optional logo displayed in the chat header. |
| greeting | string | Hi there 👋 | The smaller greeting message shown above the headline. |
| headline | string | How can we help? | The main heading text in the chat header. |