Docs
Chat Component

Chat Component

An animated chat component that can be used to display chat messages.

components/snippet/chat-component.tsx
"use client"
 
import { FC, ReactNode, useState } from "react"
import { motion } from "framer-motion"
import { MessagesSquare, XIcon } from "lucide-react"
 
import { cn } from "@/lib/utils"
 
const CONTAINER_SIZE = 200
 
interface ChatButtonProps {
  children: React.ReactNode
}
 
const ChatButton: React.FC<ChatButtonProps> = ({ children }) => {
  const [isExpanded, setIsExpanded] = useState(false)
  const toggleExpand = () => setIsExpanded(!isExpanded)
 
  return (
    <div
      className={cn(
        "rounded-[24px] border border-black shadow-sm dark:border-black/20",
        "bg-gradient-to-b from-zinc-900 to-black",
        isExpanded
          ? "bg-gradient-to-b dark:from-zinc-900/80 dark:to-zinc-900/90"
          : "dark:from-neutral-900/10 dark:to-black/10 bg-gradient-to-b rounded-full"
      )}
    >
      <ChatButtonContainer
        isExpanded={isExpanded}
        toggleExpand={toggleExpand}
      >
        {isExpanded ? (
          <motion.div
            initial={{ opacity: 0 }}
            animate={{
              opacity: 1,
              transition: {
                delay: 0.3,
                duration: 0.4,
                ease: "easeOut",
              },
            }}
          >
            {children}
          </motion.div>
        ) : null}
      </ChatButtonContainer>
    </div>
  )
}
 
// A container that wraps content and handles animations
interface ChatButtonContainerProps {
  isExpanded: boolean
  toggleExpand: () => void
  children: ReactNode
}
 
const ChatButtonContainer: FC<ChatButtonContainerProps> = ({
  isExpanded,
  toggleExpand,
  children,
}) => {
  return (
    <motion.div
      className={cn(
        "relative   border-black/10 border shadow-lg flex flex-col space-y-1  items-center  text-white  cursor-pointer z-10",
        !isExpanded
          ? "bg-gradient-to-b from-black to-black/90 dark:from-black dark:to-black/90"
          : ""
      )}
      layoutRoot
      layout
      initial={{ borderRadius: 211, width: "4rem", height: "4rem" }}
      animate={
        isExpanded
          ? {
            borderRadius: 20,
            width: CONTAINER_SIZE,
            height: CONTAINER_SIZE + 50,
 
            transition: {
              type: "spring",
              damping: 25,
              stiffness: 400,
              when: "beforeChildren",
            },
          }
          : {
            borderRadius: 211,
            width: "4rem",
            height: "4rem",
          }
      }
    >
      {children}
 
      <motion.div
        className="absolute"
        initial={{ x: "-50%" }}
        animate={{
          x: isExpanded ? "0%" : "-50%",
          transition: {
            type: "tween",
            ease: "easeOut",
            duration: 0.3,
          },
        }}
        style={{
          left: isExpanded ? "" : "50%",
          bottom: 6,
        }}
      >
        {isExpanded ? (
          <motion.div
            className="p-[10px] group bg-neutral-800/50 dark:bg-black/50 border border-cyan-100/30 hover:border-neutral-200 text-orange-50 rounded-full shadow-2xl transition-colors duration-300 "
            onClick={toggleExpand}
            layoutId="expand-toggle"
            initial={false}
            animate={{
              rotate: -360,
              transition: {
                duration: 0.4,
              },
            }}
          >
            <XIcon
              className={cn(
                "h-7 w-7 text-cyan-100/30 dark:text-neutral-400/80 group-hover:text-neutral-500 transition-colors duration-200 "
              )}
            />
          </motion.div>
        ) : (
          <motion.div
            className={cn(
              "p-[10px] group bg-neutral-200 text-cyan-50 border border-cyan-100/10  shadow-2xl transition-colors duration-200"
            )}
            style={{ borderRadius: 24 }}
            onClick={toggleExpand}
            layoutId="expand-toggle"
            initial={{ rotate: 360 }}
            animate={{
              rotate: 0,
              transition: {
                duration: 0.4,
              },
            }}
          >
            <MessagesSquare className="h-7 w-7 text-black dark:text-neutral-900" />
          </motion.div>
        )}
      </motion.div>
    </motion.div>
  )
}
 
export { ChatButton }