Refined UI

An opinionated, customizable superset of shadcn/ui

AgentsPrimitivesSkillsSkins
Integration
Guides
  • Overview
  • Get started
  • Shadcn compatibility
  • Architecture
  • Agent surface
Agents
  • Action Bar
  • Audio Recorder
  • Audio VisualizerBetaBeta — the props contract is close to final, but small breaking changes can still land.
  • Chat Input
  • Chat Input Attachment
  • Chat Message
  • Chat Scene
  • Code Block Editor
  • Dynamic NotificationExpExperimental — the contract and the rendering can change without notice. Own the installed copy before shipping it.
  • Environment Variables
  • Inline Attachment
  • Markdown Block
  • Task List
  • Thread Rail
  • Tool Call
  • User Ask
Blocks
  • Chat
  • Theme toggle
  1. Agents
  2. ChatScene
Agents

ChatScene

Layout primitives for full chat threads, turns, and thoughts.

Composition

The preferred shape for composing the installed agent from its exported parts.

Parts

Exported compound parts from the installed source.

ChatScene
├── ChatThread
├── ChatTurn
└── ChatThought

This agent installs from the chat registry. Install the bundle with the command above, or inspect the source below.

Registry command
npx shadcn@latest add http://127.0.0.1:3000/r/refined/chat.json
See registry manifest

Usage

import type { ReactNode } from "react";
​
import { ChatScene, ChatThought, ChatThread, ChatTurn } from "@/components/refined-ui/chat-scene";
​
export function Example({ children }: { children: ReactNode }) {
return (
<ChatScene>
<ChatThread>
<ChatTurn from="assistant">
<ChatThought />
{children}
</ChatTurn>
</ChatThread>
</ChatScene>
);
}
​

Raw code

Primary installed agent source

Component
src/registry/sources/refined/chat-scene.tsx
import type { ComponentProps, ElementType, ReactNode } from "react";
import { cn } from "@/components/refined-ui/lib/cn";
import { skinAdornment, skinSlot } from "@/components/refined-ui/skin";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/refined-ui/ui/collapsible";
​
type ChatThoughtCollapsible = {
Root: ElementType;
Trigger: ElementType;
Content: ElementType;
};
​
export type ChatSceneProps = ComponentProps<"section">;
​
export function ChatScene({ children, className, ...props }: ChatSceneProps) {
return (
<section
data-ui="agent"
data-component="chat-scene"
className={cn(
// Elevation "raised": shadow-md, one tier above sidebar's shadow-sm; border carries definition when --shadow-size flattens to 0 (flat DA).
"relative mx-auto flex min-h-[640px] w-full max-w-3xl flex-col overflow-hidden rounded-scene border bg-background shadow-md",
skinSlot("chat-scene", {}),
className,
)}
{...props}
>
{skinAdornment("chat-scene:titlebar", {})}
{children}
</section>
);
}
​
export type ChatThreadProps = ComponentProps<"div">;
​
export function ChatThread({ children, className, ...props }: ChatThreadProps) {
return (
<div
data-ui="agent"
data-component="chat-thread"
className={cn("flex min-h-0 flex-1 flex-col gap-8 overflow-y-auto px-4 py-6 sm:px-8", skinSlot("chat-thread", {}), className)}
{...props}
>
{children}
</div>
);
}
​
export type ChatTurnProps = ComponentProps<"section"> & {
from: "user" | "assistant";
};
​
export function ChatTurn({ from, children, className, ...props }: ChatTurnProps) {
return (
<section
data-ui="agent"
data-component="chat-turn"
data-from={from}
className={cn("group/turn flex w-full flex-col", from === "user" && "items-end", className)}
{...props}
>
{children}
</section>
);
}
​
export type ChatThoughtProps = ComponentProps<"div"> & {
details?: ReactNode;
defaultOpen?: boolean;
collapsible?: ChatThoughtCollapsible;
};
​
export function ChatThought({
children = "Thought for a couple of seconds",
details = "Read the attachment, grouped the notes by intent, and kept the final markdown compact enough to scan.",
defaultOpen = false,
collapsible,
className,
...props
}: ChatThoughtProps) {
const Root = collapsible?.Root ?? Collapsible;
const Trigger = collapsible?.Trigger ?? CollapsibleTrigger;
const Content = collapsible?.Content ?? CollapsibleContent;
​
return (
<Root
defaultOpen={defaultOpen}
data-ui="agent"
data-component="chat-thought-root"
className={cn("mb-2 self-start", className)}
{...props}
>
<Trigger
type="button"
data-ui="agent"
data-component="chat-thought"
className={cn(
"inline-flex items-center gap-1 rounded-[var(--radius-control)] px-1.5 py-1 text-meta font-medium text-muted-foreground transition hover:bg-foreground/4 hover:text-foreground [&[data-state=open]>span]:rotate-180",
skinSlot("chat-thought", {}),
)}
>
{children}
<span
aria-hidden="true"
className="text-sm leading-none transition-transform duration-[var(--duration-base)] ease-[var(--ease-emphasized)]"
>
v
</span>
</Trigger>
<Content>
{skinAdornment("chat-thought:details", {})}
<div
className={cn(
"mt-1.5 max-w-[28rem] rounded-2xl border border-foreground/8 bg-foreground/3 px-3 py-2 text-meta leading-5 text-muted-foreground",
skinSlot("chat-thought-details", {}),
)}
>
{details}
</div>
</Content>
</Root>
);
}
​

Primitives

This agent composes these library primitives, installed under components/refined-ui/ui/*.

Collapsiblecomponents/refined-ui/ui/collapsible.tsxLibrary slot

On this page

  • Preview
  • Composition
  • Installation
  • Usage
  • Source
  • Primitives
Can this scene compose turns?
Mastra
Yes. The scene owns layout while message, action bar, and composer stay separate.