Refined UI

An opinionated, customizable superset of shadcn/ui

AgentsPrimitivesSkillsSkins
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. Guides
  2. Get started
Guide

Get started

Install one component or a complete chat block, then keep the generated files in your app.

Choose the setup

Pick the runner example in the sidebar. Mastra and AI SDK examples show mapping code, while the installed UI works with any runner that can pass props and children. The site-wide skin is a separate choice in the theme editor.

Mastra is the recommended integration example.
Coming from shadcn? The library keeps shadcn's core token names and component idioms - see the shadcn compatibility guide.
Every preview, usage snippet, and raw source tab renders the exact installed files; the theme editor's skin restyles everything on top.

Install agents

Install the agent bundle when you want to compose the chat surface yourself.

npx shadcn@latest add http://127.0.0.1:3000/r/refined/chat.json

Install a block

Install the chat block when you want a complete recipe that already composes scene, messages, input, attachments, markdown, tools, and actions.

npx shadcn@latest add http://127.0.0.1:3000/r/refined/chat-block.json

Wire the CSS

npx shadcn add writes the CSS files into app/ but does not import them. Wire them once in globals.css, in this canonical order, then stamp the skin scope on <html>.

/* app/globals.css */
@import "tailwindcss";
@import "./refined-ui-theme.css"; /* the token contract + Tailwind bindings */
@import "./refined-ui-effects.css"; /* token-driven @utility effects */
​
/* only when a skin pack is installed: */
@import "./refined-ui-skin-theme.css"; /* the pack's contract re-values */
@import "./refined-ui-skin.css"; /* the pack's own vars + component CSS */
Branding YOUR app needs no pack at all: re-value tokens in a plain :root { } / .dark { } block in your own CSS - the stock shadcn convention. The baseline authors its defaults at zero specificity (:where(:root)), so your values always win; the core color tokens are shadcn's exact names.
Order barely matters for the pack files: their tokens are scoped to [data-skin="<id>"], which always outranks the baseline's zero-specificity :where(:root) defaults. Keep the canonical order anyway - Tailwind, baseline theme, effects, then the pack - so "later wins" stays true for anything unscoped.
Activate a pack by stamping data-skin="<id>" on <html> (or on any container to scope the look to a subtree). Portal-rendered popups re-assert the id on themselves, so floating surfaces stay themed either way.
Honor the pack's declared intents on the same element: colorScheme forces and locks the .dark class for fixed-scheme skins (Shiki themes, color-scheme, and dark: utilities all key off it), and motion: "reduced" stamps data-motion="reduced".
If the active skin is persisted client-side (a theme switcher), apply these attributes in a pre-paint inline script so the very first frame is already correct. THEME_INIT_SCRIPT in this repo's components/theme.ts is the reference implementation - these docs run it as a beforeInteractive script.

Map runner data

Use the examples as mapping references when your data comes from Mastra or AI SDK. Use the base component files directly when your app already maps data into Refined UI contracts.

import { ChatMessage, ChatMessageBody, ChatMessageContent, ChatMessageRow } from "@/components/refined-ui/chat-message";
​
export function Message({ text }: { text: string }) {
return (
<ChatMessage from="assistant">
<ChatMessageRow>
<ChatMessageBody>
<ChatMessageContent>{text}</ChatMessageContent>
</ChatMessageBody>
</ChatMessageRow>
</ChatMessage>
);
}

On this page

  • Choose the setup
  • Install agents
  • Install a block
  • Wire the CSS
  • Map runner data