Chat bento card
100xui/chat-bento-cardFreeBlock
A Bento chat interface card that mimics live chat interactions, great for embedding conversational UI demos in a Bento grid.
Live preview
live · rendered by the registry
Rendered by 100xui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://100xui.com/r/chat-bento-card.jsonSource
1"use client";23import * as React from "react";4import {5 AnimatePresence,6 motion,7 stagger,8 type HTMLMotionProps,9} from "motion/react";10import { CheckCheckIcon } from "lucide-react";1112import { cn } from "@/lib/utils";13import { Avatar, AvatarImage, AvatarFallback } from "./ui/avatar";1415interface OutgoingMessageType {16 type: "outgoing";17 content: React.ReactNode;18}1920interface IncomingMessageType {21 type: "incoming";22 content: React.ReactNode;23 from: string;24 avatarProps: { src?: string | Blob; fallback: string };25}2627export type ChatMessageType = OutgoingMessageType | IncomingMessageType;2829const MotionAvatar = motion.create(Avatar);3031interface ChatBentoCardProps extends React.ComponentPropsWithoutRef<"div"> {32 messages: ChatMessageType[];33 staggerMessagesInSec?: number;34 typingDurationInSec?: number;35 initialMessagesCount?: number;36 timestamp?: React.ReactNode;37 viewOptions?: IntersectionObserverInit;38 startChatOn?: "hover" | "view";39}4041const POP_LAYOUT_SYNC_DURATION = 0.2;4243export function ChatBentoCard({44 messages,45 className,46 startChatOn = "view",47 initialMessagesCount = 1,48 staggerMessagesInSec = 2,49 typingDurationInSec = 0.75,50 timestamp = "Today",51 onMouseEnter,52 onMouseLeave,53 viewOptions = { threshold: 1 },54 ...rest55}: ChatBentoCardProps) {56 const id = React.useId();5758 const [totalMessagesAnimated, setTotalMessagesAnimated] = React.useState(0);59 const [typing, setTyping] = React.useState(true);60 const intervalRef = React.useRef<NodeJS.Timeout>(undefined);6162 const chatContainerRef = React.useRef<HTMLDivElement>(null);63 const upcomingMessage = React.useRef<number>(initialMessagesCount);6465 const startAnimation = React.useCallback(() => {66 const advanceMessage = () => {67 if (messages[upcomingMessage.current]) {68 setTotalMessagesAnimated((prev) => prev + 1);69 upcomingMessage.current += 1;70 setTyping(true);71 } else {72 clearInterval(intervalRef.current);73 }74 };75 advanceMessage();76 intervalRef.current = setInterval(77 advanceMessage,78 staggerMessagesInSec * 1000,79 );80 }, [messages, staggerMessagesInSec]);8182 const resetAnimation = () => {83 setTotalMessagesAnimated(0);84 setTyping(true);85 upcomingMessage.current = 0;86 clearInterval(intervalRef.current);87 };8889 React.useEffect(() => {90 if (startChatOn !== "view") return;9192 const observer = new IntersectionObserver(([entry]) => {93 if (entry.isIntersecting) startAnimation();94// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from 100xui
All 14 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Apple galleryapple-gallery | 100xui | Blocks | Free | 1 dep | |
| demosdemos | 100xui | Blocks | Free | no deps · 12 files | |
| In-page navbarin-page-navbar | 100xui | Blocks | Free | 1 dep · 2 files | |
| internalsinternals | 100xui | Blocks | Free | no deps · 4 files | |
| iPhone 3D mockupiphone-3d-mockup | 100xui | Blocks | Free | no deps | |
| Menu wheelmenu-wheel | 100xui | Blocks | Free | 1 dep | |
| Morph modalmorph-modal | 100xui | Blocks | Free | 1 dep | |
| Motion Dockmotion-dock | 100xui | Blocks | Free | 1 dep · 3 files |
