Floating chat launcher that opens a docked panel built on Base UI Dialog, pinned to the viewport or a container, with optional scrim and header, body and footer parts.
The source comes with full access.
One payment of $149 ($49 for now) opens the source right here and lets the CLI install every paid component, with lifetime updates.
pnpm dlx shadcn add @01orbital/chat-docknpx shadcn add @01orbital/chat-dockyarn shadcn add @01orbital/chat-dockbunx --bun shadcn add @01orbital/chat-dockPart of the 01orbital library. With a license, put ORBITAL_TOKEN in .env.local and the same command installs it (licensed install, pricing).
It lands at components/chat/chat-dock.tsx, as source you own and edit.
The example also uses chat-composer, chat-conversation, chat-message and chat-response. Install them too:
pnpm dlx shadcn add @01orbital/chat-composer @01orbital/chat-conversation @01orbital/chat-message @01orbital/chat-responsenpx shadcn add @01orbital/chat-composer @01orbital/chat-conversation @01orbital/chat-message @01orbital/chat-responseyarn shadcn add @01orbital/chat-composer @01orbital/chat-conversation @01orbital/chat-message @01orbital/chat-responsebunx --bun shadcn add @01orbital/chat-composer @01orbital/chat-conversation @01orbital/chat-message @01orbital/chat-response"use client"
import * as React from "react"
import { ChatComposer } from "@/components/chat/chat-composer"
import {
ChatConversation,
ChatConversationContent,
ChatConversationTurn,
} from "@/components/chat/chat-conversation"
import {
ChatDock,
ChatDockBody,
ChatDockFooter,
ChatDockHeader,
ChatDockTitle,
} from "@/components/chat/chat-dock"
import { ChatMessage } from "@/components/chat/chat-message"
import { ChatResponse } from "@/components/chat/chat-response"
// Every word on the page. Change copy here, not in the markup.
const COPY = {
title: "Ask Orbcore",
question: "Can I change the copy and the layout?",
answer:
"Every block keeps its words in a constant at the top of the file and its layout in named parts.",
placeholder: "Ask anything…",
} as const
// The dock, open, anchored to this frame instead of the viewport.
export default function ChatDockDemo() {
const frameRef = React.useRef<HTMLDivElement>(null)
const [open, setOpen] = React.useState(true)
const [sent, setSent] = React.useState<string[]>([])
return (
<div ref={frameRef} className="relative h-144 w-full overflow-hidden">
<ChatDock
open={open}
onOpenChange={setOpen}
anchor="container"
container={frameRef}
>
<ChatDockHeader>
<ChatDockTitle>{COPY.title}</ChatDockTitle>
</ChatDockHeader>
<ChatDockBody className="flex flex-col">
<ChatConversation className="px-4 py-4">
<ChatConversationContent className="gap-6">
<ChatConversationTurn messageId="usr-opening" scrollAnchor>
<ChatMessage variant="mine">{COPY.question}</ChatMessage>
</ChatConversationTurn>
<ChatConversationTurn messageId="asst-opening">
<ChatResponse
className="text-sm/7 text-foreground"
text={COPY.answer}
/>
</ChatConversationTurn>
{sent.map((line, i) => (
<ChatConversationTurn
key={i}
messageId={`sent-${i}`}
scrollAnchor
>
<ChatMessage variant="mine">{line}</ChatMessage>
</ChatConversationTurn>
))}
</ChatConversationContent>
</ChatConversation>
</ChatDockBody>
<ChatDockFooter>
<ChatComposer
onSend={(value) => setSent((prev) => [...prev, value])}
onStop={() => {}}
streaming={false}
placeholder={COPY.placeholder}
/>
</ChatDockFooter>
</ChatDock>
</div>
)
}Chat Dock is built from 5 parts. In the example they nest like this:
ChatDock
├─ ChatDockHeader
│ └─ ChatDockTitle
├─ ChatDockBody
└─ ChatDockFooterChatDock: A floating chat: a round launcher in the corner that opens a chat panel above it. Put ChatDockHeader, ChatDockBody and ChatDockFooter inside.ChatDockHeader: The panel's top bar: your title on the left and a close button on the right.ChatDockTitle: The panel's title, which also names the dialog for screen readers.ChatDockBody: The panel's middle, which fills the space between header and footer; put the conversation here.ChatDockFooter: The panel's bottom bar, above a thin rule; put the composer here.A floating chat: a round launcher in the corner that opens a chat panel above it. Put ChatDockHeader, ChatDockBody and ChatDockFooter inside.
Prop
Type
The panel's top bar: your title on the left and a close button on the right.
It accepts the HTML attributes of the element it renders.
The panel's title, which also names the dialog for screen readers.
It accepts the props of the Base UI part it wraps, and the HTML attributes of the element it renders.
The panel's middle, which fills the space between header and footer; put the conversation here.
It accepts the HTML attributes of the element it renders.
The panel's bottom bar, above a thin rule; put the composer here.
It accepts the HTML attributes of the element it renders.
npm: @base-ui/react, @phosphor-icons/react, cn. The CLI installs them for you.
Registry pieces it installs with it:
buttonChat Conversation
Scrolling chat transcript built on Message Scroller: a polite live log that follows the streaming edge, with turn, attribution, pulsing cursor and shimmering status parts.
Chat Inline Component Card
Frame for a live registry component inside a chat reply: a mono caption with category, name and open link over a scaled-down preview. Includes helpers for [[name]] citations.