Advanced Examples
Real-world examples demonstrating sophisticated BotUI usage patterns for complex conversational interfaces.
1. Multi-Step Form Bot
A bot that collects user information through a conversational form with validation and conditional logic.
import React, { useEffect } from 'react'
import { createBot } from 'botui'
import { BotUI, BotUIMessageList, BotUIAction, useBotUI, useBotUIAction } from '@botui/react'
const FormBot = () => {
const myBot = createBot()
// Validation plugin
const validationPlugin = (block) => {
if (block.type === 'action' && block.meta.validation) {
// Add validation metadata for custom action renderers
block.meta.hasValidation = true
}
return block
}
myBot.use(validationPlugin)
const collectUserData = async () => {
const userData = {}
// Welcome message
await myBot.message.add({
text: 'Welcome! I\'ll help you create your profile. Let\'s start with some basic information.'
})
// Collect name with validation
await myBot.message.add({ text: 'What\'s your full name?' })
const nameResponse = await myBot.action.set(
{
placeholder: 'Enter your full name',
pattern: '^[a-zA-Z\\s]{2,50}$'
},
{
actionType: 'validatedInput',
validation: {
required: true,
minLength: 2,
errorMessage: 'Please enter a valid name (2-50 characters, letters only)'
}
}
)
userData.name = nameResponse.value
// Collect email
await myBot.message.add({ text: 'What\'s your email address?' })
const emailResponse = await myBot.action.set(
{
placeholder: '[email protected]',
type: 'email'
},
{
actionType: 'validatedInput',
validation: {
required: true,
pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
errorMessage: 'Please enter a valid email address'
}
}
)
userData.email = emailResponse.value
// Collect age and show conditional content
await myBot.message.add({ text: 'How old are you?' })
const ageResponse = await myBot.action.set(
{
placeholder: 'Age',
type: 'number',
min: 13,
max: 120
},
{
actionType: 'validatedInput',
validation: {
required: true,
min: 13,
max: 120,
errorMessage: 'Age must be between 13 and 120'
}
}
)
userData.age = parseInt(ageResponse.value)
// Conditional content based on age
if (userData.age < 18) {
await myBot.message.add({
text: 'Since you\'re under 18, we\'ll need parental consent.'
})
const consentResponse = await myBot.action.set(
{
options: [
{ label: 'I have parental consent', value: 'yes' },
{ label: 'I need to get permission', value: 'no' }
]
},
{ actionType: 'select' }
)
userData.parentalConsent = consentResponse.selected.value
if (userData.parentalConsent === 'no') {
await myBot.message.add({
text: 'Please get parental permission and come back to complete your registration.'
})
return
}
}
// Collect interests
await myBot.message.add({ text: 'What are your interests? (Select all that apply)' })
const interestsResponse = await myBot.action.set(
{
isMultiSelect: true,
options: [
{ label: 'Technology', value: 'tech' },
{ label: 'Sports', value: 'sports' },
{ label: 'Music', value: 'music' },
{ label: 'Travel', value: 'travel' },
{ label: 'Gaming', value: 'gaming' },
{ label: 'Reading', value: 'reading' }
]
},
{ actionType: 'select' }
)
userData.interests = interestsResponse.selected.map(item => item.value)
// Show profile summary
await myBot.message.add(
{
profile: userData
},
{ messageType: 'profileSummary' }
)
// Confirmation
const confirmResponse = await myBot.action.set(
{
options: [
{ label: 'Looks good!', value: 'confirm' },
{ label: 'Let me edit', value: 'edit' }
]
},
{ actionType: 'selectButtons' }
)
if (confirmResponse.selected.value === 'confirm') {
await myBot.message.add({
text: `Perfect! Welcome aboard, ${userData.name}! 🎉`
})
// Save data to backend
console.log('Saving user data:', userData)
} else {
await myBot.message.add({
text: 'No problem! Let\'s start over.'
})
// Restart the process
setTimeout(() => collectUserData(), 1000)
}
}
useEffect(() => {
collectUserData()
}, [])
return (
<BotUI bot={myBot}>
<BotUIMessageList renderer={customRenderers} />
<BotUIAction renderer={customRenderers} />
</BotUI>
)
}
// Custom renderers
const ValidatedInput = () => {
const bot = useBotUI()
const action = useBotUIAction()
const [value, setValue] = React.useState('')
const [error, setError] = React.useState('')
const validate = (inputValue) => {
const validation = action.meta.validation
if (!validation) return true
if (validation.required && !inputValue.trim()) {
setError(validation.errorMessage || 'This field is required')
return false
}
if (validation.minLength && inputValue.length < validation.minLength) {
setError(validation.errorMessage || `Minimum ${validation.minLength} characters required`)
return false
}
if (validation.pattern && !new RegExp(validation.pattern).test(inputValue)) {
setError(validation.errorMessage || 'Invalid format')
return false
}
return true
}
const handleSubmit = () => {
if (validate(value)) {
bot.next({ value })
}
}
return (
<div className="validated-input">
<input
type={action.data.type || 'text'}
value={value}
onChange={(e) => {
setValue(e.target.value)
setError('')
}}
placeholder={action.data.placeholder}
min={action.data.min}
max={action.data.max}
/>
<button onClick={handleSubmit}>Continue</button>
{error && <div className="error">{error}</div>}
</div>
)
}
const ProfileSummary = ({ message }) => {
const { profile } = message.data
return (
<div className="profile-summary">
<h3>Profile Summary</h3>
<div><strong>Name:</strong> {profile.name}</div>
<div><strong>Email:</strong> {profile.email}</div>
<div><strong>Age:</strong> {profile.age}</div>
<div><strong>Interests:</strong> {profile.interests.join(', ')}</div>
</div>
)
}
const customRenderers = {
validatedInput: ValidatedInput,
profileSummary: ProfileSummary
}
2. E-commerce Shopping Bot
A sophisticated shopping assistant that helps users browse products, add to cart, and checkout.
const ShoppingBot = () => {
const myBot = createBot()
// Mock product data
const products = [
{ id: 1, name: 'Wireless Headphones', price: 99.99, category: 'electronics', image: '/headphones.jpg' },
{ id: 2, name: 'Running Shoes', price: 129.99, category: 'sports', image: '/shoes.jpg' },
{ id: 3, name: 'Coffee Mug', price: 19.99, category: 'home', image: '/mug.jpg' }
]
let cart = []
const startShopping = async () => {
await myBot.message.add({
text: 'Welcome to our store! 🛒 How can I help you today?'
})
const intentResponse = await myBot.action.set(
{
options: [
{ label: 'Browse products', value: 'browse' },
{ label: 'Search for something specific', value: 'search' },
{ label: 'View my cart', value: 'cart' }
]
},
{ actionType: 'selectButtons' }
)
switch (intentResponse.selected.value) {
case 'browse':
await browseProducts()
break
case 'search':
await searchProducts()
break
case 'cart':
await viewCart()
break
}
}
const browseProducts = async () => {
await myBot.message.add({ text: 'Here are our featured products:' })
// Show products
await myBot.message.add(
{ products: products.slice(0, 3) },
{ messageType: 'productGrid' }
)
const productResponse = await myBot.action.set(
{
products: products.slice(0, 3),
cart: cart
},
{ actionType: 'productSelection' }
)
if (productResponse.action === 'addToCart') {
const product = products.find(p => p.id === productResponse.productId)
cart.push({ ...product, quantity: 1 })
await myBot.message.add({
text: `Added ${product.name} to your cart! 🎉`
})
}
await continueShoppingPrompt()
}
const searchProducts = async () => {
await myBot.message.add({ text: 'What are you looking for?' })
const searchResponse = await myBot.action.set(
{ placeholder: 'Search products...' },
{ actionType: 'input' }
)
const searchTerm = searchResponse.value.toLowerCase()
const results = products.filter(p =>
p.name.toLowerCase().includes(searchTerm) ||
p.category.toLowerCase().includes(searchTerm)
)
if (results.length > 0) {
await myBot.message.add({
text: `Found ${results.length} product(s) matching "${searchTerm}":`
})
await myBot.message.add(
{ products: results },
{ messageType: 'productGrid' }
)
} else {
await myBot.message.add({
text: `Sorry, no products found for "${searchTerm}". Try a different search term.`
})
}
await continueShoppingPrompt()
}
const viewCart = async () => {
if (cart.length === 0) {
await myBot.message.add({ text: 'Your cart is empty. Let\'s find some products!' })
await browseProducts()
return
}
const total = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0)
await myBot.message.add(
{ cartItems: cart, total },
{ messageType: 'cartSummary' }
)
const cartResponse = await myBot.action.set(
{
options: [
{ label: 'Proceed to Checkout', value: 'checkout' },
{ label: 'Continue Shopping', value: 'continue' },
{ label: 'Clear Cart', value: 'clear' }
]
},
{ actionType: 'selectButtons' }
)
switch (cartResponse.selected.value) {
case 'checkout':
await checkout()
break
case 'continue':
await browseProducts()
break
case 'clear':
cart = []
await myBot.message.add({ text: 'Cart cleared!' })
await startShopping()
break
}
}
const checkout = async () => {
const total = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0)
await myBot.message.add({
text: `Great! Let's complete your order. Total: $${total.toFixed(2)}`
})
// Collect shipping info
await myBot.message.add({ text: 'What\'s your shipping address?' })
const addressResponse = await myBot.action.set(
{ placeholder: 'Enter your full address' },
{ actionType: 'input' }
)
// Payment method
await myBot.message.add({ text: 'Choose your payment method:' })
const paymentResponse = await myBot.action.set(
{
options: [
{ label: 'Credit Card', value: 'credit' },
{ label: 'PayPal', value: 'paypal' },
{ label: 'Apple Pay', value: 'apple' }
]
},
{ actionType: 'select' }
)
// Order confirmation
await myBot.message.add(
{
orderSummary: {
items: cart,
total,
shipping: addressResponse.value,
payment: paymentResponse.selected.label
}
},
{ messageType: 'orderConfirmation' }
)
const confirmResponse = await myBot.action.set(
{
options: [
{ label: 'Place Order', value: 'confirm' },
{ label: 'Go Back', value: 'back' }
]
},
{ actionType: 'selectButtons' }
)
if (confirmResponse.selected.value === 'confirm') {
await myBot.message.add({
text: '🎉 Order placed successfully! You\'ll receive a confirmation email shortly.'
})
cart = [] // Clear cart
} else {
await viewCart()
}
}
const continueShoppingPrompt = async () => {
const continueResponse = await myBot.action.set(
{
options: [
{ label: 'Browse more products', value: 'browse' },
{ label: 'Search again', value: 'search' },
{ label: 'View cart', value: 'cart' },
{ label: 'I\'m done', value: 'done' }
]
},
{ actionType: 'selectButtons' }
)
switch (continueResponse.selected.value) {
case 'browse':
await browseProducts()
break
case 'search':
await searchProducts()
break
case 'cart':
await viewCart()
break
case 'done':
await myBot.message.add({ text: 'Thanks for shopping with us! Come back soon! 👋' })
break
}
}
useEffect(() => {
startShopping()
}, [])
return (
<BotUI bot={myBot}>
<BotUIMessageList renderer={shoppingRenderers} />
<BotUIAction renderer={shoppingRenderers} />
</BotUI>
)
}
// Shopping-specific renderers
const ProductGrid = ({ message }) => {
const { products } = message.data
return (
<div className="product-grid">
{products.map(product => (
<div key={product.id} className="product-card">
<img src={product.image} alt={product.name} />
<h4>{product.name}</h4>
<p>${product.price}</p>
</div>
))}
</div>
)
}
const ProductSelection = () => {
const bot = useBotUI()
const action = useBotUIAction()
const { products } = action.data
const handleAddToCart = (productId) => {
bot.next({ action: 'addToCart', productId })
}
return (
<div className="product-selection">
{products.map(product => (
<div key={product.id} className="product-action-card">
<span>{product.name} - ${product.price}</span>
<button onClick={() => handleAddToCart(product.id)}>
Add to Cart
</button>
</div>
))}
</div>
)
}
const CartSummary = ({ message }) => {
const { cartItems, total } = message.data
return (
<div className="cart-summary">
<h3>Your Cart</h3>
{cartItems.map((item, index) => (
<div key={index} className="cart-item">
<span>{item.name}</span>
<span>Qty: {item.quantity}</span>
<span>${(item.price * item.quantity).toFixed(2)}</span>
</div>
))}
<div className="cart-total">
<strong>Total: ${total.toFixed(2)}</strong>
</div>
</div>
)
}
const shoppingRenderers = {
productGrid: ProductGrid,
productSelection: ProductSelection,
cartSummary: CartSummary
}
3. AI Assistant with Context Memory
An advanced AI assistant that maintains conversation context and provides intelligent responses.
const AIAssistantBot = () => {
const myBot = createBot()
// Context management
let conversationContext = {
user: {},
currentTopic: null,
history: [],
preferences: {}
}
// Context plugin
const contextPlugin = (block) => {
if (block.type === 'message') {
// Store conversation history
conversationContext.history.push({
type: 'bot',
content: block.data.text,
timestamp: new Date().toISOString()
})
}
return block
}
myBot.use(contextPlugin)
const startConversation = async () => {
await myBot.message.add({
text: 'Hi! I\'m your AI assistant. I can help you with various tasks. What would you like to do today?'
})
await showMainMenu()
}
const showMainMenu = async () => {
const menuResponse = await myBot.action.set(
{
options: [
{ label: '📝 Take notes', value: 'notes' },
{ label: '🧮 Calculate something', value: 'calculate' },
{ label: '📅 Schedule reminder', value: 'reminder' },
{ label: '🤖 Ask me anything', value: 'chat' },
{ label: '⚙️ Settings', value: 'settings' }
]
},
{ actionType: 'selectButtons' }
)
conversationContext.currentTopic = menuResponse.selected.value
switch (menuResponse.selected.value) {
case 'notes':
await handleNotes()
break
case 'calculate':
await handleCalculation()
break
case 'reminder':
await handleReminder()
break
case 'chat':
await handleChat()
break
case 'settings':
await handleSettings()
break
}
}
const handleNotes = async () => {
await myBot.message.add({ text: 'I\'ll help you take notes. What would you like to note down?' })
const noteResponse = await myBot.action.set(
{ placeholder: 'Type your note here...' },
{ actionType: 'textarea' }
)
const note = {
id: Date.now(),
content: noteResponse.value,
timestamp: new Date().toISOString(),
tags: []
}
// Ask for tags
await myBot.message.add({ text: 'Would you like to add tags to categorize this note?' })
const tagResponse = await myBot.action.set(
{ placeholder: 'Enter tags separated by commas (optional)' },
{ actionType: 'input' }
)
if (tagResponse.value.trim()) {
note.tags = tagResponse.value.split(',').map(tag => tag.trim())
}
// Save note (in real app, this would go to a database)
conversationContext.notes = conversationContext.notes || []
conversationContext.notes.push(note)
await myBot.message.add(
{ note },
{ messageType: 'noteSaved' }
)
await continuationPrompt()
}
const handleCalculation = async () => {
await myBot.message.add({
text: 'I can help you with calculations. Enter a mathematical expression:'
})
const calcResponse = await myBot.action.set(
{
placeholder: 'e.g., 2 + 2 * 3, sqrt(16), 15% of 200',
validation: true
},
{ actionType: 'calculatorInput' }
)
try {
// In a real app, use a proper math parser
const result = evaluateExpression(calcResponse.value)
await myBot.message.add(
{
expression: calcResponse.value,
result: result
},
{ messageType: 'calculationResult' }
)
} catch (error) {
await myBot.message.add({
text: 'Sorry, I couldn\'t calculate that. Please check your expression and try again.'
})
}
await continuationPrompt()
}
const handleReminder = async () => {
await myBot.message.add({ text: 'What would you like to be reminded about?' })
const taskResponse = await myBot.action.set(
{ placeholder: 'Enter your reminder' },
{ actionType: 'input' }
)
await myBot.message.add({ text: 'When should I remind you?' })
const timeResponse = await myBot.action.set(
{
options: [
{ label: 'In 1 hour', value: '1h' },
{ label: 'Tomorrow', value: '1d' },
{ label: 'Next week', value: '1w' },
{ label: 'Custom time', value: 'custom' }
]
},
{ actionType: 'select' }
)
let reminderTime
if (timeResponse.selected.value === 'custom') {
await myBot.message.add({ text: 'Enter the date and time (e.g., "2024-12-25 14:30"):' })
const customTimeResponse = await myBot.action.set(
{ placeholder: 'YYYY-MM-DD HH:MM', type: 'datetime-local' },
{ actionType: 'input' }
)
reminderTime = new Date(customTimeResponse.value)
} else {
const timeMap = { '1h': 1, '1d': 24, '1w': 168 }
reminderTime = new Date(Date.now() + timeMap[timeResponse.selected.value] * 60 * 60 * 1000)
}
const reminder = {
id: Date.now(),
task: taskResponse.value,
time: reminderTime,
created: new Date().toISOString()
}
conversationContext.reminders = conversationContext.reminders || []
conversationContext.reminders.push(reminder)
await myBot.message.add(
{ reminder },
{ messageType: 'reminderSet' }
)
await continuationPrompt()
}
const continuationPrompt = async () => {
const continueResponse = await myBot.action.set(
{
options: [
{ label: 'Main menu', value: 'menu' },
{ label: 'More ' + conversationContext.currentTopic, value: 'more' },
{ label: 'Exit', value: 'exit' }
]
},
{ actionType: 'selectButtons' }
)
switch (continueResponse.selected.value) {
case 'menu':
await showMainMenu()
break
case 'more':
// Repeat current topic
await showMainMenu() // Could be more sophisticated
break
case 'exit':
await myBot.message.add({ text: 'Goodbye! Feel free to ask for help anytime. 👋' })
break
}
}
useEffect(() => {
startConversation()
}, [])
return (
<BotUI bot={myBot}>
<BotUIMessageList renderer={assistantRenderers} />
<BotUIAction renderer={assistantRenderers} />
</BotUI>
)
}
// Helper function for calculations
const evaluateExpression = (expr) => {
// This is a simplified evaluator - use a proper math library in production
const sanitized = expr.replace(/[^0-9+\-*/().\s]/g, '')
return Function('"use strict"; return (' + sanitized + ')')()
}
// Custom renderers for AI Assistant
const NoteSaved = ({ message }) => {
const { note } = message.data
return (
<div className="note-saved">
<h4>✅ Note Saved</h4>
<div className="note-content">{note.content}</div>
{note.tags.length > 0 && (
<div className="note-tags">
{note.tags.map(tag => (
<span key={tag} className="tag">#{tag}</span>
))}
</div>
)}
<small>Saved at {new Date(note.timestamp).toLocaleString()}</small>
</div>
)
}
const CalculationResult = ({ message }) => {
const { expression, result } = message.data
return (
<div className="calculation-result">
<div className="expression">{expression}</div>
<div className="equals">=</div>
<div className="result">{result}</div>
</div>
)
}
const ReminderSet = ({ message }) => {
const { reminder } = message.data
return (
<div className="reminder-set">
<h4>⏰ Reminder Set</h4>
<div><strong>Task:</strong> {reminder.task}</div>
<div><strong>Time:</strong> {new Date(reminder.time).toLocaleString()}</div>
</div>
)
}
const assistantRenderers = {
noteSaved: NoteSaved,
calculationResult: CalculationResult,
reminderSet: ReminderSet
}
These examples demonstrate:
- Multi-step forms with validation and conditional logic
- E-commerce flows with product browsing, cart management, and checkout
- AI assistants with context memory and multiple capabilities
- Custom renderers for specialized UI components
- Complex state management across conversation flows
- Error handling and validation patterns
- Real-world data structures and API integration patterns
Each example shows how BotUI can be extended to create sophisticated conversational interfaces that go far beyond simple question-and-answer patterns.