at-bubble
npx shadcn@latest add @manifest/quick-replies
npx shadcn@latest add @manifest/payment-block
#### Step 2: Component Composition
Create a chat interface that dynamically renders blocks based on the message type. The following example demonstrates a customer support scenario where the bot can send text, quick replies, and payment requests.
```tsx
import { useState } from 'react';
import { ChatStream } from '@/components/manifest/chat-stream';
import { MessageBubble } from '@/components/manifest/message-bubble';
import { QuickReplies } from '@/components/manifest/quick-replies';
import { PaymentBlock } from '@/components/manifest/payment-block';
import { Avatar } from '@/components/manifest/avatar';
interface ChatMessage {
id: string;
role: 'user' | 'assistant';
content: string;
timestamp: Date;
actions?: MessageAction[];
}
type MessageAction =
| { type: 'quick_reply'; options: string[] }
| { type: 'payment'; amount: number; currency: string; description: string };
export function AISupportInterface() {
const [messages, setMessages] = useState<ChatMessage[]>([
{
id: '1',
role: 'assistant',
content: 'How can I help you today?',
timestamp: new Date(),
actions: [
{ type: 'quick_reply', options: ['Check Order', 'Refund Status', 'Contact Agent'] }
]
}
]);
const handleAction = (action: MessageAction) => {
// Logic to handle user selection
console.log('Action triggered:', action);
};
return (
<div className="flex h-screen flex-col bg-background">
<header className="border-b p-4">
<h1 className="text-lg font-semibold">Support Assistant</h1>
</header>
<ChatStream className="flex-1 overflow-y-auto p-4">
{messages.map((msg) => (
<div key={msg.id} className={`flex gap-3 ${msg.role === 'user' ? 'flex-row-reverse' : ''}`}>
<Avatar
src={msg.role === 'assistant' ? '/bot-avatar.png' : undefined}
fallback={msg.role === 'assistant' ? 'AI' : 'U'}
className="h-8 w-8"
/>
<div className="flex flex-col gap-2 max-w-[80%]">
<MessageBubble
content={msg.content}
variant={msg.role === 'user' ? 'primary' : 'secondary'}
timestamp={msg.timestamp}
/>
{msg.actions?.map((action, idx) => {
if (action.type === 'quick_reply') {
return (
<QuickReplies
key={idx}
options={action.options}
onSelect={(option) => handleAction({ type: 'quick_reply', options: [option] })}
/>
);
}
if (action.type === 'payment') {
return (
<PaymentBlock
key={idx}
amount={action.amount}
currency={action.currency}
description={action.description}
onConfirm={() => handleAction(action)}
/>
);
}
return null;
})}
</div>
</div>
))}
</ChatStream>
<div className="border-t p-4">
{/* Input area component */}
</div>
</div>
);
}
Architecture Decisions
- Block-Based Installation: Using
npx shadcn@latest add ensures you only include necessary code. This prevents bloat and allows you to modify components directly in your codebase.
- TypeScript Props: All blocks expose strict TypeScript interfaces, enabling compile-time safety when handling complex payloads like payment details or event metadata.
- Tailwind CSS v4 & Custom Properties: Theming is managed via CSS variables, allowing seamless integration with existing design systems. Dark mode is handled automatically through these variables.
- Composability: Blocks like
QuickReplies and PaymentBlock are designed to sit alongside MessageBubble, enabling rich, multi-modal responses without custom layout logic.
Pitfall Guide
Even with pre-built blocks, implementing AI chat interfaces requires careful attention to state, performance, and user experience.
1. State Synchronization Drift
Issue: The UI state diverges from the LLM context, causing the bot to lose track of user selections made via quick replies or forms.
Fix: Maintain a single source of truth for the conversation history. When a user interacts with a block, immediately append the action to the message list and send the corresponding payload to the backend. Ensure the UI reflects the action as "processed" to prevent duplicate submissions.
2. Overloading the Chat Stream
Issue: Rendering too many interactive elements (e.g., multiple payment blocks, long option lists) clutters the interface and overwhelms the user.
Fix: Implement a "progressive disclosure" strategy. Show only relevant actions based on the current conversation turn. Use pagination or collapsible sections for long lists. Limit quick replies to 3-4 options per turn.
3. Ignoring Loading States for Async Actions
Issue: Users click a payment button or submit a form without visual feedback, leading to confusion or repeated clicks.
Fix: All interactive blocks must support loading states. Disable buttons and show spinners during API calls. Provide success/error toasts upon completion. Manifest UI blocks support loading props; ensure you wire these to your async handlers.
4. Accessibility Neglect in Chat Bubbles
Issue: Chat interfaces often fail accessibility standards, particularly with screen readers and keyboard navigation.
Fix: Ensure all interactive elements have proper aria-labels. Use role="log" for the chat stream with aria-live="polite" to announce new messages. Verify that quick replies and buttons are focusable and operable via keyboard. Test with screen readers to confirm reading order.
5. Theme Inconsistency with CSS Variables
Issue: Customizing blocks breaks the dark mode or conflicts with the global theme.
Fix: Rely on Tailwind CSS custom properties for colors and spacing. Avoid hardcoding values in block styles. When extending blocks, use the @apply directive or class composition to maintain consistency. Verify that all CSS variables are defined in both light and dark modes.
6. Version Drift in shadcn/ui
Issue: Updating shadcn/ui or Manifest UI blocks introduces breaking changes or style regressions.
Fix: Pin versions in your package.json and review changelogs before updating. Since blocks are copied into your project, you can diff changes manually. Maintain a backup of customized blocks to merge updates safely.
7. Missing Error Boundaries
Issue: A rendering error in a single message block crashes the entire chat interface.
Fix: Wrap the chat stream in an error boundary. Implement fallback UI for individual messages that fail to render. Log errors to your monitoring system for debugging. This ensures the rest of the conversation remains accessible even if one block fails.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Simple Q&A Bot | Use MessageBubble + QuickReplies | Minimal overhead; fast implementation. | Low |
| Transactional Chat | Add PaymentBlock + FormBlock | Enables in-chat purchases without redirects. | Medium |
| Event Booking | Use EventCard + TagSelector | Structured data display for dates/times. | Medium |
| Rich Media Support | Include SocialEmbed + FileUpload | Handles images, videos, and documents. | High |
| Custom Branding | Extend blocks via CSS variables | Maintains consistency while matching brand. | Low |
Configuration Template
Use this template to configure Tailwind CSS v4 for Manifest UI blocks. Ensure all variables are defined for both light and dark modes.
@import "tailwindcss";
@theme {
--color-background: #ffffff;
--color-foreground: #0f172a;
--color-primary: #2563eb;
--color-primary-foreground: #ffffff;
--color-muted: #f1f5f9;
--color-muted-foreground: #64748b;
--color-border: #e2e8f0;
--color-ring: #2563eb;
--radius-sm: 0.25rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
}
.dark {
--color-background: #0f172a;
--color-foreground: #f8fafc;
--color-primary: #3b82f6;
--color-primary-foreground: #ffffff;
--color-muted: #1e293b;
--color-muted-foreground: #94a3b8;
--color-border: #334155;
--color-ring: #3b82f6;
}
Quick Start Guide
- Install Blocks: Run
npx shadcn@latest add @manifest/chat-bubble and @manifest/quick-replies in your terminal.
- Create Interface: Copy the
AISupportInterface component from the Core Solution and adapt it to your message schema.
- Add Styles: Ensure your CSS includes the theme variables from the Configuration Template.
- Test Locally: Start your dev server and verify that messages render correctly with avatars, timestamps, and interactive elements.
- Deploy: Build your project and deploy. Verify dark mode and responsive behavior in production.