Text-to-UI Conversion
Cranberrry's powerful text-to-UI conversion system transforms structured text responses from your LLM into rich, interactive React components. This system uses custom tags and component mapping to create dynamic user interfaces that update in real-time as responses stream in.
How Text-to-UI Works
The text-to-UI conversion process follows these steps:
- LLM Response: Your AI generates structured text with custom tags
- Tag Parsing: Cranberrry's parser detects and extracts tagged content
- Data Processing: JSON content is parsed, text content is preserved
- Component Mapping: Each tag maps to a specific React component
- Real-time Rendering: Components update as new chunks arrive
Core Conversion Process
1. Text Response Structure
Your LLM should structure responses with custom tags that define UI elements:
<conversation>Hello! I'll help you build a React component.</conversation>
<code-analysis>{"language": "javascript", "issues": ["missing semicolon"], "suggestions": ["Add semicolon"]}</code-analysis>
<progress-update>{"step": 1, "total": 3, "message": "Analyzing code...", "percentage": 33}</progress-update>
<file-operation>{"action": "create", "filename": "Button.jsx", "content": "import React from 'react'", "status": "success"}</file-operation>
2. Component Mapping
Each tag maps to a React component that renders the structured data:
const tagConfigs: CBTagConfig[] = [
{
tag: "conversation",
processor: "TEXT",
component: ConversationBlock
},
{
tag: "code-analysis",
processor: "JSON",
component: CodeAnalysisBlock
},
{
tag: "progress-update",
processor: "JSON",
component: ProgressUpdateBlock
},
{
tag: "file-operation",
processor: "JSON",
component: FileOperationBlock
},
];
3. Real-time Updates
Components update automatically as new content streams in:
const {
startAgentTask,
parseChunk,
isRunning,
complete,
} = useCBController({
agentId: agent.id,
tagConfigs,
callbacks: {
onBlockProcessed: (block: CBMessageBlock) => {
// Component automatically renders new block
console.log('New UI component rendered:', block);
},
},
});
Custom Tag Implementation
Creating Custom Tags
Define tags that match your application's UI needs:
<!-- Code Analysis with Issues -->
<code-analysis>{"language": "javascript", "issues": ["missing semicolon", "unused variable"], "suggestions": ["Add semicolon", "Remove unused variable"]}</code-analysis>
<!-- Progress Updates -->
<progress-update>{"step": 3, "total": 5, "message": "Installing dependencies...", "percentage": 60}</progress-update>
<!-- Data Visualization -->
<chart-data>{"type": "bar", "labels": ["Jan", "Feb", "Mar"], "values": [10, 20, 15], "title": "Monthly Sales"}</chart-data>
<!-- User Input Forms -->
<form-input>{"type": "text", "label": "Project Name", "placeholder": "Enter project name", "required": true}</form-input>
<!-- File Operations -->
<file-operation>{"action": "create", "filename": "app.js", "content": "console.log('Hello World')", "status": "success"}</file-operation>
Building Custom React Components
Create components that render your structured data with rich UI:
// Code Analysis Component
const CodeAnalysisBlock = ({ ai }: { ai: any }) => (
<div className="bg-red-50 border border-red-200 rounded-lg p-4 dark:bg-red-900/20 dark:border-red-800">
<div className="flex items-center gap-2 mb-3">
<CodeIcon className="h-5 w-5 text-red-600" />
<h3 className="font-medium text-red-900 dark:text-red-100">Code Analysis</h3>
</div>
<div className="space-y-2">
<p className="text-sm text-red-800 dark:text-red-200">
Language: {ai.language}
</p>
{ai.issues.length > 0 && (
<div>
<p className="text-xs font-medium text-red-700 dark:text-red-300 mb-1">Issues Found:</p>
<ul className="text-xs text-red-700 dark:text-red-300 space-y-1">
{ai.issues.map((issue: string, index: number) => (
<li key={index} className="flex items-start gap-1">
<span className="text-red-600 dark:text-red-400">⚠</span>
<span>{issue}</span>
</li>
))}
</ul>
</div>
)}
{ai.suggestions.length > 0 && (
<div>
<p className="text-xs font-medium text-green-700 dark:text-green-300 mb-1">Suggestions:</p>
<ul className="text-xs text-green-700 dark:text-green-300 space-y-1">
{ai.suggestions.map((suggestion: string, index: number) => (
<li key={index} className="flex items-start gap-1">
<span className="text-green-600 dark:text-green-400">💡</span>
<span>{suggestion}</span>
</li>
))}
</ul>
</div>
)}
</div>
</div>
);
// Progress Update Component
const ProgressUpdateBlock = ({ ai }: { ai: any }) => (
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4 dark:bg-blue-900/20 dark:border-blue-800">
<div className="flex items-center justify-between mb-2">
<span className="text-sm font-medium text-blue-900 dark:text-blue-100">
Step {ai.step} of {ai.total}
</span>
<span className="text-sm text-blue-700 dark:text-blue-300">
{ai.percentage}%
</span>
</div>
<div className="w-full bg-blue-200 rounded-full h-2 dark:bg-blue-800">
<div
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
style={{ width: `${ai.percentage}%` }}
/>
</div>
<p className="text-sm text-blue-800 dark:text-blue-200 mt-2">
{ai.message}
</p>
</div>
);
// Chart Data Component
const ChartDataBlock = ({ ai }: { ai: any }) => (
<div className="bg-purple-50 border border-purple-200 rounded-lg p-4 dark:bg-purple-900/20 dark:border-purple-800">
<h3 className="font-medium text-purple-900 dark:text-purple-100 mb-3">
{ai.title}
</h3>
<div className="flex items-end gap-2 h-32">
{ai.labels.map((label: string, index: number) => (
<div key={index} className="flex-1 flex flex-col items-center">
<div
className="w-full bg-purple-600 rounded-t transition-all duration-300"
style={{ height: `${(ai.values[index] / Math.max(...ai.values)) * 100}%` }}
/>
<span className="text-xs text-purple-700 dark:text-purple-300 mt-1">
{label}
</span>
</div>
))}
</div>
</div>
);
// Form Input Component
const FormInputBlock = ({ ai }: { ai: any }) => (
<div className="bg-gray-50 border border-gray-200 rounded-lg p-4 dark:bg-gray-900/20 dark:border-gray-800">
<label className="block text-sm font-medium text-gray-900 dark:text-gray-100 mb-2">
{ai.label}
</label>
<input
type={ai.type}
placeholder={ai.placeholder}
required={ai.required}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-600 dark:text-white"
/>
</div>
);
// File Operation Component
const FileOperationBlock = ({ ai }: { ai: any }) => (
<div className={`border rounded-lg p-4 ${
ai.status === 'success'
? 'bg-green-50 border-green-200 dark:bg-green-900/20 dark:border-green-800'
: 'bg-red-50 border-red-200 dark:bg-red-900/20 dark:border-red-800'
}`}>
<div className="flex items-center gap-2 mb-2">
<FileIcon className="h-4 w-4" />
<span className="text-sm font-medium">
{ai.action.charAt(0).toUpperCase() + ai.action.slice(1)}: {ai.filename}
</span>
<span className={`text-xs px-2 py-1 rounded-full ${
ai.status === 'success'
? 'bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-200'
: 'bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-200'
}`}>
{ai.status}
</span>
</div>
{ai.content && (
<pre className="text-xs bg-gray-100 dark:bg-gray-800 p-2 rounded mt-2 overflow-x-auto">
{ai.content}
</pre>
)}
</div>
);
## UI Conversion Examples
### Example 1: Code Review Assistant
**LLM Response:**
```xml
<conversation>I've analyzed your React component. Here are the issues I found:</conversation>
<code-analysis>{"language": "javascript", "issues": ["Missing PropTypes", "Unused import"], "suggestions": ["Add PropTypes validation", "Remove unused import"]}</code-analysis>
<file-operation>{"action": "update", "filename": "Button.jsx", "content": "import React from 'react';\nimport PropTypes from 'prop-types';", "status": "success"}</file-operation>
Generated UI:
- Conversation bubble with analysis message
- Red card showing code issues and suggestions
- Green card showing successful file update with code preview
Example 2: Project Setup Wizard
LLM Response:
<conversation>Let's set up your new React project step by step.</conversation>
<progress-update>{"step": 1, "total": 4, "message": "Creating project structure...", "percentage": 25}</progress-update>
<form-input>{"type": "text", "label": "Project Name", "placeholder": "my-react-app", "required": true}</form-input>
<progress-update>{"step": 2, "total": 4, "message": "Installing dependencies...", "percentage": 50}</progress-update>
Generated UI:
- Conversation bubble with setup instructions
- Progress bar showing current step and percentage
- Form input for project name
- Updated progress bar as steps complete
Example 3: Data Analysis Dashboard
LLM Response:
<conversation>Here's your monthly sales analysis:</conversation>
<chart-data>{"type": "bar", "labels": ["Jan", "Feb", "Mar", "Apr"], "values": [12000, 15000, 18000, 22000], "title": "Monthly Sales"}</chart-data>
<content-block>{"type": "summary", "content": "Sales increased by 83% over 4 months", "recommendations": ["Focus on Q2 marketing", "Expand product line"]}</content-block>
Generated UI:
- Conversation bubble with analysis introduction
- Purple card with animated bar chart
- Green card with summary and recommendations
Configuration and Setup
1. Register Custom Tags
Configure your tag processors in your application:
const customTagConfigs: CBTagConfig[] = [
// Built-in tags
{
tag: "conversation",
processor: "TEXT",
component: ConversationBlock
},
{
tag: "content-block",
processor: "JSON",
component: ContentBlock
},
{
tag: "action-block",
processor: "JSON",
component: ActionBlock
},
// Custom UI tags
{
tag: "code-analysis",
processor: "JSON",
component: CodeAnalysisBlock
},
{
tag: "progress-update",
processor: "JSON",
component: ProgressUpdateBlock
},
{
tag: "chart-data",
processor: "JSON",
component: ChartDataBlock
},
{
tag: "form-input",
processor: "JSON",
component: FormInputBlock
},
{
tag: "file-operation",
processor: "JSON",
component: FileOperationBlock
},
];
2. Use in Your Application
Integrate the text-to-UI conversion in your React app:
const {
startAgentTask,
parseChunk,
isRunning,
complete,
reset,
} = useCBController({
agentId: agent.id,
tagConfigs: customTagConfigs,
callbacks: {
onBlockProcessed: (block: CBMessageBlock) => {
console.log('New UI component rendered:', block);
},
onBlockFound: (tag: string) => {
console.log('Found UI tag:', tag);
},
onComplete: () => {
console.log('UI conversion complete');
},
onError: (error: string) => {
console.error('UI conversion error:', error);
},
},
});
3. Render the UI
Use the CranberrryRenderer to display the converted UI:
import { CranberrryRenderer } from 'cranberrry';
function MyApp() {
return (
<div className="space-y-4">
<CranberrryRenderer taskId={currentTaskId} />
</div>
);
}
Domain-Specific UI Examples
E-commerce Application
<product-recommendation>{"category": "electronics", "products": [{"name": "iPhone 15", "price": 999, "rating": 4.5}], "reason": "Based on your browsing history"}</product-recommendation>
<shopping-cart-update>{"items": 3, "total": 299.99, "savings": 50.00}</shopping-cart-update>
<order-status>{"orderId": "12345", "status": "shipped", "tracking": "1Z999AA1234567890"}</order-status>
Code Review Application
<code-review>{"file": "app.js", "lines": [10, 15], "severity": "warning", "message": "Consider using const instead of let"}</code-review>
<security-alert>{"type": "vulnerability", "package": "lodash", "version": "4.17.15", "cve": "CVE-2021-23337"}</security-alert>
<performance-metric>{"metric": "loadTime", "value": 2.3, "unit": "seconds", "threshold": 3.0}</performance-metric>
Project Management Application
<task-assignment>{"assignee": "john@example.com", "priority": "high", "deadline": "2024-01-15", "description": "Implement user authentication"}</task-assignment>
<milestone-update>{"milestone": "MVP Release", "progress": 75, "tasks": [{"name": "Frontend", "status": "completed"}, {"name": "Backend", "status": "in-progress"}]}</milestone-update>
<team-notification>{"type": "mention", "user": "alice", "message": "Please review the latest changes"}</team-notification>
Best Practices for Text-to-UI
1. Tag Design
- Use descriptive tag names that clearly indicate the UI component type
- Keep JSON structure simple and avoid deeply nested objects
- Include a type field in your JSON for better component logic
- Use consistent naming conventions across your application
2. Component Design
- Handle missing data gracefully in your components
- Use proper TypeScript types for better development experience
- Implement responsive design for different screen sizes
- Add loading states for better user experience
- Use consistent styling with your design system
3. Performance
- Optimize component rendering to handle frequent updates
- Use React.memo for components that don't need frequent re-renders
- Implement proper key props for list rendering
- Consider lazy loading for complex components
4. Testing
- Test your custom components with various data scenarios
- Mock the parsing system for unit tests
- Test real-time updates with streaming data
- Validate JSON structure before rendering
Summary
Cranberrry's text-to-UI conversion system provides:
- Flexible tag system - Create unlimited custom tags for your UI needs
- Rich component library - Build beautiful, interactive React components
- Real-time updates - UI components update as responses stream in
- Domain-specific solutions - Tailor the system to your application's needs
- Type-safe development - Full TypeScript support for better DX
- Performance optimized - Efficient parsing and rendering system
By leveraging this system, you can create dynamic, AI-powered user interfaces that respond to user input in real-time, providing rich and engaging user experiences.