Conversation Thread
godui/conversation-threadFreeComponent
A streaming AI chat surface where messages spring in, tokens reveal with a blinking caret, hover actions fade in, and the view auto-sticks to the latest message.
Install it
npx shadcn@latest add https://godui.design/r/conversation-thread.jsonSource
1"use client";23import { AnimatePresence, motion, useReducedMotion } from "framer-motion";4import * as React from "react";56export type ConversationVariant = "bubbles" | "document" | "compact";7export type MessageRole = "user" | "assistant" | "system";89type ThreadContextValue = { variant: ConversationVariant };10const ThreadContext = React.createContext<ThreadContextValue>({11 variant: "bubbles",12});1314export type ConversationThreadProps = React.HTMLAttributes<HTMLDivElement> & {15 variant?: ConversationVariant;16 /** Auto-stick to the newest message unless the user scrolls up. */17 autoScroll?: boolean;18};1920const THREAD_BASE =21 "relative flex h-full flex-col gap-4 overflow-y-auto px-4 py-4";2223const ConversationThread = React.forwardRef<24 HTMLDivElement,25 ConversationThreadProps26>(27 (28 { variant = "bubbles", autoScroll = true, className, children, ...props },29 forwardedRef,30 ) => {31 const ref = React.useRef<HTMLDivElement>(null);32 const contentRef = React.useRef<HTMLDivElement>(null);33 React.useImperativeHandle(34 forwardedRef,35 () => ref.current as HTMLDivElement,36 );37 const [pinned, setPinned] = React.useState(true);38 // Ref mirror so ResizeObserver / effects never read a stale `pinned`.39 const pinnedRef = React.useRef(true);4041 const setPinnedBoth = React.useCallback((next: boolean) => {42 pinnedRef.current = next;43 setPinned(next);44 }, []);4546 const scrollToBottom = React.useCallback((behavior: ScrollBehavior) => {47 const el = ref.current;48 if (!el) return;49 // `instant` keeps streaming growth glued without fighting layout;50 // `smooth` is reserved for the intentional Jump-to-latest click.51 if (typeof el.scrollTo === "function") {52 el.scrollTo({ top: el.scrollHeight, behavior });53 } else {54 el.scrollTop = el.scrollHeight;55 }56 }, []);5758 // Stick to the bottom when a new message mounts.59 const childCount = React.Children.count(children);60 // biome-ignore lint/correctness/useExhaustiveDependencies: re-pin when a message is added61 React.useEffect(() => {62 if (autoScroll && pinnedRef.current) scrollToBottom("instant");63 }, [childCount, autoScroll, scrollToBottom]);6465 // Stick through *content* growth too (StreamingText ticks, wrapping) —66 // childCount alone misses that. Observe the inner stack, not the scroll67 // port: ResizeObserver on an overflow container does not fire when only68 // scrollHeight grows.69// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from godui
All 105 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | godui | Components | Free | 1 dep | |
| Agent Flowagent-flow | godui | Components | Free | 1 dep | |
| Agent Timelineagent-timeline | godui | Components | Free | 1 dep | |
| Animated Beamanimated-beam | godui | Components | Free | 1 dep | |
| Animated Testimonialsanimated-testimonials | godui | Components | Free | 1 dep | |
| Animated Tooltipanimated-tooltip | godui | Components | Free | 1 dep | |
| App Showcaseapp-showcase | godui | Components | Free | 1 dep | |
| Aurora Textaurora-text | godui | Components | Free | no deps |
