
Syncfusion React Chat Ui
- 446 installs
- 3 repo stars
- Updated July 28, 2026
- syncfusion/react-ui-components-skills
syncfusion-react-chat-ui is a Syncfusion Agent Skill that teaches developers to implement the React Chat UI component with message rendering, file attachments, typing indicators, mentions, and RTL localization using offi
About
syncfusion-react-chat-ui is a component-aware Agent Skill (version 33.1.44) from syncfusion/react-ui-components-skills for building production React messaging interfaces. The SKILL.md maps eleven reference guides covering getting started, message management, user configuration, events, templating, file attachments, header customization, timestamps, typing indicators, mentions, and globalization. Quick-start TSX shows ChatUIComponent with MessagesDirective, UserModel authors, and headerText configuration. Common patterns include support chats with enableAttachments and messageToolbarSettings, team collaboration with @mentions and compact mode, and multilingual RTL layouts. Developers reach for syncfusion-react-chat-ui when agents must generate correct Syncfusion imports, addMessage() calls, and attachment event handlers instead of hallucinating undocumented Chat UI props.
- syncfusion-react-chat-ui
Syncfusion React Chat Ui by the numbers
- 446 all-time installs (skills.sh)
- +53 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #974 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/react-ui-components-skills --skill syncfusion-react-chat-uiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 446 |
|---|---|
| repo stars | ★ 3 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/react-ui-components-skills ↗ |
How do you build a React chat UI with Syncfusion?
Use syncfusion-react-chat-ui for development tasks
Who is it for?
React developers adding customer-support or team chat interfaces with Syncfusion Essential Studio who need accurate component APIs and event wiring.
Skip if: Custom chat UIs built from scratch without Syncfusion licensing or projects using non-React frameworks like Vue or Angular.
When should I use this skill?
The user asks to implement Syncfusion React Chat UI, messaging interfaces, chat attachments, or typing indicators in a React app.
What you get
ChatUIComponent TSX with MessagesDirective, user models, attachment handlers, typing-indicator templates, and mention configuration.
- ChatUIComponent TSX implementation
- Message and attachment event handlers
- Custom message templates
By the numbers
- Targets Syncfusion React Chat UI version 33.1.44
- Ships 11 feature-specific reference implementation guides
Files
Syncfusion React Chat-UI in Syncfusion React
Component Overview
The Chat-UI component provides a complete conversational interface with real-time messaging, file attachments, user presence, and extensive customization options. This skill guides you through implementing chat interfaces for customer support, team collaboration, and interactive applications.
Key Capabilities
The Chat-UI component is an interactive messaging interface featuring:
- Rich Message Support: Text, markdown, formatted content
- User Management: Multi-user conversations, presence status, avatars
- File Attachments: Upload, preview, download with restrictions
- Event System: Lifecycle, interaction, and attachment events
- Templates: Customizable UI for messages, suggestions, footer
- Internationalization: Multi-language and RTL support
Documentation Navigation
Getting Started
📄 Read: references/getting-started.md
- Package installation
- Basic component initialization
- CSS theming and imports
Message Management
📄 Read: references/message-management.md
- Adding and updating messages
- Auto-scroll to latest messages
- Progressive message loading (on-demand)
- Message configuration (pinned, reply, forward)
- Message status tracking
- Message toolbar customization
User Configuration
📄 Read: references/user-configuration.md
- User model setup
- Avatar customization
- User presence status
- CSS class customization
Events and Interactions
📄 Read: references/events-and-interactions.md
- Component lifecycle events
- Message sending events
- User typing events (with complete event arguments)
- Attachment upload and click events
- Toolbar click events
Templating System
📄 Read: references/templating-system.md
- Empty chat template
- Message custom rendering
- Time break template
- Typing indicator template
- Suggestion template
- Footer template customization
File Attachments
📄 Read: references/file-attachments.md
- Enable attachment support
- Configure upload endpoints
- File type and size restrictions
- Drag-and-drop support
- Attachment templates (preview and footer)
- Handle attachment click events
Header Customization
📄 Read: references/header-customization.md
- Header visibility and text
- Header icon styling
- Toolbar configuration
- Toolbar item types and events
Appearance and Styling
📄 Read: references/appearance-styling.md
- Pompact mode layout for dense conversations
- Claceholder text configuration
- Width and height customization
- CSS class application
- Component-level styling
Timestamp and Time Breaks
📄 Read: references/timestamp-and-timebreaks.md
- Show/hide timestamps
- Timestamp format customization
- Time break separators
- Date-wise organization
Typing Indicator
📄 Read: references/typing-indicator.md
- Show/hide typing indicator
- Multi-user typing display
- Typing indicator template customization
Mention Integration
📄 Read: references/mention-integration.md
- Configure mentionable users
- Customize mention trigger character
- Predefined mentions in messages
- Mention selection events
Globalization and RTL
📄 Read: references/globalization-rtl.md
- Localization and language support
- Right-to-Left (RTL) layout
- Typing indicator localization
Quick Start Example
import { ChatUIComponent, MessagesDirective, MessageDirective, UserModel } from '@syncfusion/ej2-react-interactive-chat';
import * as React from 'react';
import * as ReactDOM from "react-dom";
function App() {
const currentUserModel: UserModel = {
id: "user1",
user: "Albert"
};
const otherUserModel: UserModel = {
id: "user2",
user: "Michale Suyama"
};
return (
<ChatUIComponent user={currentUserModel} headerText="Chat Support">
<MessagesDirective>
<MessageDirective text="Hello, how can I help?" author={currentUserModel} />
<MessageDirective text="I need assistance with my order." author={otherUserModel} />
</MessagesDirective>
</ChatUIComponent>
);
}
ReactDOM.render(<App />, document.getElementById('container'));Common Patterns
Create a Support Chat Interface
<ChatUIComponent
user={currentUser}
headerText="Support Chat"
enableAttachments={true}
showTimeStamp={true}
showHeader={true}
messageToolbarSettings={{ items: ['Copy', 'Reply', 'Delete'] }}
>
<MessagesDirective>
{/* messages populate here */}
</MessagesDirective>
</ChatUIComponent>Add Message Dynamically
const chatRef = useRef<ChatUIComponent>(null);
const addNewMessage = () => {
chatRef.current.addMessage({
author: userModel,
text: "Your message here",
timeStamp: new Date()
});
};Configure Multiple Templates
Implement messageTemplate, emptyChatTemplate, timeBreakTemplate, and suggestionTemplate for complete customization.
Key Props
| Prop | Type | Purpose |
|---|---|---|
user | UserModel | Current logged-in user |
messages | MessageModel[] | Initial message list |
headerText | string | Header title |
placeholder | string | Input field hint |
enableAttachments | boolean | File upload support |
showTimeStamp | boolean | Display message timestamps |
showHeader | boolean | Show header section |
showFooter | boolean | Show input footer |
autoScrollToBottom | boolean | Auto-scroll to latest message |
loadOnDemand | boolean | Progressive message loading |
enableCompactMode | boolean | Left-aligned compact layout |
enableRtl | boolean | Right-to-left layout |
locale | string | Language/culture code |
Common Use Cases
Use Case 1: Customer Support Chat
- Multiple support agents handling customer queries
- File attachment for screenshots and auto-scroll
- Typing indicators to show agent engagement
- Progressive loading for long conversation histories
- Typing indicators to show agent engagement
Use Case 2: Team Collaboration
- Mention team members with @
- Compact mode for group conversations
- Pin important discussions
- Forward messages for reference
- Message editing and deletion
Use Case 3: Multilingual Chat Application
- RTL support for Arabic/Hebrew
- Localized UI strings
- Support for multiple cultures
---
For complete examples and advanced scenarios, explore individual reference files above.
Appearance and Styling
Table of Contents
- Placeholder Text
- Width Configuration
- Height Configuration
- Compact Mode Layout
- CSS Class Application
- Theme Styling
Placeholder Text
Set Input Placeholder
Customize the hint text in the message input field:
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
placeholder="Type your message here..."
/>
);
}Default Placeholder
Default value: "Type your message…"
Custom Placeholders
// For support chat
placeholder="Describe your issue..."
// For team chat
placeholder="Share your thoughts..."
// For customer service
placeholder="How can we help?"
// Minimal
placeholder="Say something..."Width Configuration
Set Component Width
Control the horizontal size:
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
return (
<>
{/* Full width (default) */}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
width="100%"
/>
{/* Fixed width */}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
width="450px"
/>
{/* Viewport width */}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
width="80vw"
/>
</>
);
}Width Variations
// Small sidebar
width="320px"
// Medium panel
width="500px"
// Large panel
width="700px"
// Responsive
width="100%"
// Container percentage
width="90%"Height Configuration
Set Component Height
Control the vertical size:
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
return (
<>
{/* Full height (default) */}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
height="100%"
/>
{/* Fixed height */}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
height="500px"
/>
{/* Viewport height */}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
height="80vh"
/>
</>
);
}Height Variations
// Small window
height="300px"
// Medium window
height="500px"
// Large window
height="700px"
// Full screen
height="100vh"
// Fill container
height="100%"Responsive Height
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React, { useState, useEffect } from 'react';
function App() {
const [height, setHeight] = useState('100vh');
useEffect(() => {
const handleResize = () => {
const headerHeight = 60;
const footerHeight = 50;
const newHeight = window.innerHeight - headerHeight - footerHeight;
setHeight(`${newHeight}px`);
};
window.addEventListener('resize', handleResize);
handleResize();
return () => window.removeEventListener('resize', handleResize);
}, []);
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
height={height}
/>
);
}Compact Mode Layout
Enable Compact Mode
Display all messages aligned to the left side, creating a simplified chat view ideal for dense group conversations or compact displays:
import { ChatUIComponent, MessagesDirective, MessageDirective } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const user = { id: "user1", user: "Albert" };
const agent = { id: "user2", user: "Support" };
return (
<ChatUIComponent
user={user}
enableCompactMode={true} // Enable compact layout
>
<MessagesDirective>
<MessageDirective text="My message" author={user} />
<MessageDirective text="Agent response" author={agent} />
</MessagesDirective>
</ChatUIComponent>
);
}Default Mode vs Compact Mode
Default Mode (enableCompactMode={false}):
- Current user messages aligned to the right
- Other users' messages aligned to the left
- Clear visual distinction between participants
Compact Mode (enableCompactMode={true}):
- All messages aligned to the left
- More space-efficient layout
- Better for group chats with many participants
- Ideal for mobile or embedded displays
Use Case: Mobile Chat
import { ChatUIComponent, MessagesDirective, MessageDirective } from '@syncfusion/ej2-react-interactive-chat';
import React, { useState, useEffect } from 'react';
function App() {
const [isCompact, setIsCompact] = useState(false);
useEffect(() => {
// Enable compact mode on smaller screens
const handleResize = () => {
setIsCompact(window.innerWidth < 768);
};
handleResize();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableCompactMode={isCompact}
height="100vh"
>
<MessagesDirective>
<MessageDirective
text="Message 1"
author={{ id: "user1", user: "Albert" }}
/>
<MessageDirective
text="Message 2"
author={{ id: "user2", user: "Support" }}
/>
</MessagesDirective>
</ChatUIComponent>
);
}Use Case: Group Chat
Compact mode is particularly useful for group conversations with multiple participants:
import { ChatUIComponent, MessagesDirective, MessageDirective, UserModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const users: Record<string, UserModel> = {
user1: { id: "user1", user: "Albert", avatarBgColor: "#0c888e" },
user2: { id: "user2", user: "Michale", avatarBgColor: "#e3165b" },
user3: { id: "user3", user: "Reena", avatarBgColor: "#6d3e94" },
user4: { id: "user4", user: "John", avatarBgColor: "#3c78d8" }
};
return (
<ChatUIComponent
user={users.user1}
headerText="Team Discussion"
enableCompactMode={true} // Better for group chats
height="600px"
>
<MessagesDirective>
<MessageDirective text="Let's discuss the project" author={users.user1} />
<MessageDirective text="I have some updates" author={users.user2} />
<MessageDirective text="Great! Let's hear them" author={users.user3} />
<MessageDirective text="I'll share my findings too" author={users.user4} />
</MessagesDirective>
</ChatUIComponent>
);
}Custom Styling for Compact Mode
/* Adjust spacing in compact mode */
.e-chat-ui.e-compact-mode .e-message {
margin-bottom: 8px;
}
.e-chat-ui.e-compact-mode .e-message-bubble {
max-width: 85%;
}
.e-chat-ui.e-compact-mode .e-avatar {
width: 32px;
height: 32px;
}Toggle Compact Mode
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React, { useState } from 'react';
function App() {
const [compactMode, setCompactMode] = useState(false);
return (
<div>
<div style={{ padding: '10px', marginBottom: '10px' }}>
<label>
<input
type="checkbox"
checked={compactMode}
onChange={(e) => setCompactMode(e.target.checked)}
/>
{' '}Enable Compact Mode
</label>
</div>
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableCompactMode={compactMode}
height="500px"
/>
</div>
);
}CSS Class Application
Apply Custom CSS Class
Add custom styling via CSS classes:
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
import './custom-chat.css';
function App() {
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
cssClass="custom-chat-container"
/>
);
}Custom CSS Styles
Create a custom-chat.css file:
/* Main container styling */
.custom-chat-container {
border: 1px solid #e0e0e0;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
background-color: #ffffff;
}
/* Header styling */
.custom-chat-container .e-chat-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 16px;
border-radius: 8px 8px 0 0;
}
.custom-chat-container .e-chat-header-text {
color: white;
font-weight: 600;
}
/* Message area */
.custom-chat-container .e-chat-messages {
padding: 16px;
background-color: #fafafa;
}
/* Input footer */
.custom-chat-container .e-footer {
padding: 12px;
background-color: #ffffff;
border-top: 1px solid #e0e0e0;
}
.custom-chat-container .e-input-group {
border: 1px solid #ddd;
border-radius: 4px;
}
/* Message bubbles */
.custom-chat-container .e-chat-message {
margin-bottom: 12px;
}
.custom-chat-container .e-message-right {
text-align: right;
}
.custom-chat-container .e-message-left {
text-align: left;
}Theme Styling
Material Theme (Default)
@import "../node_modules/@syncfusion/ej2-react-interactive-chat/styles/material.css";Bootstrap 5 Theme
@import "../node_modules/@syncfusion/ej2-react-interactive-chat/styles/bootstrap5.css";Fluent Theme
@import "../node_modules/@syncfusion/ej2-react-interactive-chat/styles/fluent.css";Tailwind Theme
@import "../node_modules/@syncfusion/ej2-react-interactive-chat/styles/tailwind.css";High Contrast Theme
@import "../node_modules/@syncfusion/ej2-react-interactive-chat/styles/highcontrast.css";Complete Styling Example
Integrated Styling
import { ChatUIComponent, MessagesDirective, MessageDirective } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
import './app-styles.css';
function App() {
return (
<div className="chat-wrapper">
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
headerText="Support Chat"
placeholder="Ask me anything..."
width="100%"
height="600px"
cssClass="app-chat-style"
>
<MessagesDirective>
<MessageDirective
text="Hello! How can I assist you today?"
author={{ id: "agent", user: "Support Agent" }}
/>
</MessagesDirective>
</ChatUIComponent>
</div>
);
}
export default App;Corresponding CSS
/* Wrapper styling */
.chat-wrapper {
max-width: 900px;
margin: 0 auto;
padding: 20px;
}
/* Chat container */
.app-chat-style {
border-radius: 12px;
border: 1px solid #d0d0d0;
overflow: hidden;
transition: all 0.3s ease;
}
.app-chat-style:hover {
border-color: #0078d4;
box-shadow: 0 4px 12px rgba(0, 120, 212, 0.15);
}
/* Header customization */
.app-chat-style .e-chat-header {
background-color: #0078d4;
padding: 16px 20px;
}
.app-chat-style .e-chat-header-text {
color: white;
font-size: 16px;
font-weight: 600;
}
/* Message styling */
.app-chat-style .e-message-bubble {
border-radius: 12px;
padding: 10px 14px;
}
.app-chat-style .e-message-right {
background-color: #0078d4;
color: white;
}
.app-chat-style .e-message-left {
background-color: #f0f0f0;
color: #333;
}
/* Footer input */
.app-chat-style .e-input-group {
border-radius: 4px;
border: 1px solid #d0d0d0;
}
.app-chat-style .e-input-group input {
font-size: 14px;
}
.app-chat-style .e-send-button {
background-color: #0078d4;
border: none;
cursor: pointer;
}
.app-chat-style .e-send-button:hover {
background-color: #005a9e;
}Dark Mode Support
Enable Dark Mode
.app-chat-style.dark-mode {
background-color: #1e1e1e;
color: #ffffff;
}
.app-chat-style.dark-mode .e-chat-header {
background-color: #2d2d2d;
}
.app-chat-style.dark-mode .e-message-left {
background-color: #333333;
color: #ffffff;
}
.app-chat-style.dark-mode .e-message-right {
background-color: #0078d4;
}
.app-chat-style.dark-mode .e-input-group {
background-color: #333;
border-color: #444;
}
.app-chat-style.dark-mode .e-input-group input {
background-color: #333;
color: #fff;
}Responsive Design
Mobile and Desktop
/* Desktop */
@media (min-width: 768px) {
.chat-wrapper {
max-width: 900px;
}
.app-chat-style {
width: 600px;
height: 700px;
}
}
/* Tablet */
@media (min-width: 480px) and (max-width: 767px) {
.app-chat-style {
width: 100%;
height: 500px;
}
}
/* Mobile */
@media (max-width: 479px) {
.chat-wrapper {
padding: 0;
}
.app-chat-style {
width: 100%;
height: 100vh;
border-radius: 0;
}
}Best Practices
1. Use semantic color schemes for better UX 2. Ensure sufficient contrast for accessibility 3. Test responsive design on different devices 4. Apply consistent branding with company colors 5. Optimize for both light and dark themes 6. Maintain readable font sizes (minimum 14px) 7. Use proper spacing for visual hierarchy
Events and Interactions
Table of Contents
Component Lifecycle
Created Event
Fires after the Chat component is fully initialized:
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const handleCreated = () => {
console.log('Chat component initialized');
// Perform initialization tasks
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
created={handleCreated}
/>
);
}Message Events
Message Send Event
Triggered before sending a message:
import { ChatUIComponent, MessageSentEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const handleMessageSend = (args: MessageSentEventArgs) => {
console.log('Message being sent:', args.message.text);
// Validate message before sending
if (args.message.text.trim().length === 0) {
args.cancel = true; // Prevent empty messages
}
// Add timestamp
args.message.timeStamp = new Date();
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
messageSend={handleMessageSend}
/>
);
}Message Sent Example with Validation
const handleMessageSend = (args: MessageSentEventArgs) => {
const message = args.message.text.trim();
// Validate message content
if (message.length === 0) {
args.cancel = true;
alert('Please enter a message');
return;
}
// Check for spam/profanity
const bannedWords = ['spam', 'abuse'];
if (bannedWords.some(word => message.toLowerCase().includes(word))) {
args.cancel = true;
alert('Message contains inappropriate content');
return;
}
// Log message for analytics
console.log('User sent:', message);
};Typing Events
User Typing Event
Fires while user is typing in the input field:
import { ChatUIComponent, TypingEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import React, { useState } from 'react';
function App() {
const [isTyping, setIsTyping] = useState(false);
const handleUserTyping = (args: TypingEventArgs) => {
console.log('User is typing:', args.isTyping);
console.log('Current message:', args.message);
console.log('User:', args.user.user);
setIsTyping(args.isTyping);
};
return (
<div>
{isTyping && <p>You are typing...</p>}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
userTyping={handleUserTyping}
/>
</div>
);
}Typing Event Arguments
The TypingEventArgs interface provides complete typing event details:
| Property | Type | Description |
|---|---|---|
event | Event | The underlying input event that triggered the typing action |
isTyping | boolean | true when user is actively typing, false when typing ends or message is sent |
message | string | Current content of the message being typed |
name | string | Name of the event ('userTyping') |
user | UserModel | The current user who is typing |
Detect Typing Status
Use isTyping to determine when user starts and stops typing:
import { ChatUIComponent, TypingEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import React, { useRef } from 'react';
function App() {
const typingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const handleUserTyping = (args: TypingEventArgs) => {
if (args.isTyping) {
console.log('User started typing');
// Notify server that user is typing
notifyServer({ userId: args.user.id, isTyping: true });
// Clear previous timeout
if (typingTimeoutRef.current) {
clearTimeout(typingTimeoutRef.current);
}
// Stop typing indicator after 2 seconds of inactivity
typingTimeoutRef.current = setTimeout(() => {
notifyServer({ userId: args.user.id, isTyping: false });
}, 2000);
} else {
console.log('User stopped typing');
notifyServer({ userId: args.user.id, isTyping: false });
}
};
const notifyServer = (data: any) => {
// Send typing status to server via WebSocket or API
console.log('Notifying server:', data);
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
userTyping={handleUserTyping}
/>
);
}Access Message Content
Use the message property to access what the user is typing:
const handleUserTyping = (args: TypingEventArgs) => {
const messageLength = args.message.length;
console.log('Current message:', args.message);
console.log('Character count:', messageLength);
// Warn if message is too long
if (messageLength > 1000) {
console.warn('Message exceeds recommended length');
}
// Check for specific keywords
if (args.message.includes('@urgent')) {
console.log('Urgent message detected');
}
};Real-Time Typing Notifications
Broadcast typing status to other users in real-time:
import { ChatUIComponent, TypingEventArgs, UserModel } from '@syncfusion/ej2-react-interactive-chat';
import React, { useState, useEffect } from 'react';
function App() {
const [typingUsers, setTypingUsers] = useState<UserModel[]>([]);
const currentUser = { id: "user1", user: "Albert" };
const handleUserTyping = (args: TypingEventArgs) => {
// Send typing status via WebSocket
const ws = new WebSocket('ws://example.com/chat');
ws.send(JSON.stringify({
type: 'typing',
userId: args.user.id,
userName: args.user.user,
isTyping: args.isTyping,
message: args.message
}));
};
useEffect(() => {
// Listen for other users' typing status
const ws = new WebSocket('ws://example.com/chat');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'typing' && data.userId !== currentUser.id) {
if (data.isTyping) {
// Add user to typing list
setTypingUsers(prev => [
...prev.filter(u => u.id !== data.userId),
{ id: data.userId, user: data.userName }
]);
} else {
// Remove user from typing list
setTypingUsers(prev => prev.filter(u => u.id !== data.userId));
}
}
};
return () => ws.close();
}, []);
return (
<ChatUIComponent
user={currentUser}
userTyping={handleUserTyping}
typingUsers={typingUsers}
/>
);
}Complete Typing Example with Analytics
import { ChatUIComponent, TypingEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import React, { useRef } from 'react';
function App() {
const typingStartTime = useRef<number | null>(null);
const characterCount = useRef(0);
const handleUserTyping = (args: TypingEventArgs) => {
// Track when user starts typing
if (args.isTyping && !typingStartTime.current) {
typingStartTime.current = Date.now();
characterCount.current = args.message.length;
console.log('Started typing at:', new Date(typingStartTime.current));
}
// Track when user stops typing
if (!args.isTyping && typingStartTime.current) {
const duration = Date.now() - typingStartTime.current;
const finalLength = args.message.length;
const charsTyped = finalLength - characterCount.current;
// Log analytics
console.log('Typing session ended:', {
duration: `${(duration / 1000).toFixed(1)}s`,
charactersTyped: charsTyped,
wordsPerMinute: Math.round((charsTyped / 5) / (duration / 60000)),
user: args.user.user
});
// Reset tracking
typingStartTime.current = null;
characterCount.current = 0;
}
// Access current message content
console.log('Current message:', args.message);
console.log('Message length:', args.message.length);
// Access event details
if (args.event) {
console.log('Event type:', args.event.type);
}
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
userTyping={handleUserTyping}
/>
);
}Attachment Events
Before Attachment Upload
Fires before files are uploaded:
import { ChatUIComponent, FileAttachmentSettingsModel, BeforeAttachmentUploadEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const handleBeforeUpload = (args: BeforeAttachmentUploadEventArgs) => {
console.log('Uploading:', args.files);
// Validate file size
const maxSize = 5 * 1024 * 1024; // 5MB
args.files.forEach(file => {
if (file.size > maxSize) {
console.warn('File too large:', file.name);
}
});
};
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
beforeAttachmentUpload: handleBeforeUpload
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableAttachments={true}
attachmentSettings={attachmentSettings}
/>
);
}Attachment Upload Success
Fires after successful file upload:
import { ChatUIComponent, FileAttachmentSettingsModel, AttachmentUploadSuccessEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const handleUploadSuccess = (args: AttachmentUploadSuccessEventArgs) => {
console.log('File uploaded successfully:', args);
console.log('File name:', args.fileName);
console.log('Upload response:', args.response);
};
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
attachmentUploadSuccess: handleUploadSuccess
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableAttachments={true}
attachmentSettings={attachmentSettings}
/>
);
}Attachment Upload Failure
Fires when file upload fails:
const handleUploadFailure = (args: AttachmentUploadFailureEventArgs) => {
console.error('Upload failed:', args.error);
alert(`Failed to upload ${args.fileName}`);
};
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
attachmentUploadFailure: handleUploadFailure
};Attachment Removed Event
Fires when user removes an attachment:
const handleAttachmentRemoved = (args: AttachmentRemovedEventArgs) => {
console.log('Attachment removed:', args.fileName);
// Update UI or clean up
};
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
attachmentRemoved: handleAttachmentRemoved
};Attachment Click Event
Fires when user clicks on an attachment (either in the footer before sending or in a message after sending):
import { ChatUIComponent, FileAttachmentSettingsModel, ChatAttachmentClickEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const handleAttachmentClick = (args: ChatAttachmentClickEventArgs) => {
console.log('Attachment clicked:', args.file.name);
console.log('File info:', {
name: args.file.name,
type: args.file.type,
size: args.file.size,
url: args.file.url
});
// Cancel default preview behavior if needed
if (args.file.type === 'application/pdf') {
args.cancel = true;
window.open(args.file.url, '_blank');
}
};
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
attachmentClick: handleAttachmentClick
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableAttachments={true}
attachmentSettings={attachmentSettings}
/>
);
}Attachment Click Event Arguments
The ChatAttachmentClickEventArgs interface provides:
| Property | Type | Description |
|---|---|---|
cancel | boolean | Set to true to prevent default preview rendering |
event | Event | The underlying click event object |
file | FileInfo | Complete file information including name, type, size, and URL |
name | string | Name of the event ('attachmentClick') |
FileInfo Properties
The file object in the event arguments contains:
| Property | Type | Description |
|---|---|---|
name | string | Name of the file |
type | string | MIME type of the file (e.g., 'image/png', 'application/pdf') |
size | number | File size in bytes |
url | string | URL or path to access the file |
fileSource | string | Base64 or Blob source for the file |
Handle Different File Types
const handleAttachmentClick = (args: ChatAttachmentClickEventArgs) => {
const file = args.file;
// Handle images
if (file.type?.startsWith('image/')) {
showImagePreview(file.url);
args.cancel = true;
}
// Handle PDFs
else if (file.type === 'application/pdf') {
window.open(file.url, '_blank');
args.cancel = true;
}
// Handle videos
else if (file.type?.startsWith('video/')) {
playVideo(file.url);
args.cancel = true;
}
// For other files, allow default download behavior
};Toolbar Events
Toolbar Item Click Event
Fires when user clicks on message toolbar items:
import { ChatUIComponent, MessageToolbarSettingsModel, MessageToolbarItemClickedEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import React, { useRef } from 'react';
function App() {
const chatRef = useRef<ChatUIComponent>(null);
const handleToolbarClick = (args: MessageToolbarItemClickedEventArgs) => {
const icon = args.item.iconCss;
if (icon === 'e-icons e-chat-copy') {
navigator.clipboard.writeText(args.message.text);
console.log('Copied to clipboard');
}
else if (icon === 'e-icons e-chat-reply') {
console.log('Reply to message:', args.message.id);
}
else if (icon === 'e-icons e-chat-pin') {
console.log('Pin message:', args.message.id);
}
else if (icon === 'e-icons e-chat-trash') {
console.log('Delete message:', args.message.id);
}
};
const messageToolbarSettings: MessageToolbarSettingsModel = {
items: [
{ type: 'Button', iconCss: 'e-icons e-chat-copy', tooltip: 'Copy' },
{ type: 'Button', iconCss: 'e-icons e-chat-reply', tooltip: 'Reply' },
{ type: 'Button', iconCss: 'e-icons e-chat-pin', tooltip: 'Pin' },
{ type: 'Button', iconCss: 'e-icons e-chat-trash', tooltip: 'Delete' }
],
itemClicked: handleToolbarClick
};
return (
<ChatUIComponent
ref={chatRef}
user={{ id: "user1", user: "Albert" }}
messageToolbarSettings={messageToolbarSettings}
/>
);
}Header Toolbar Click Event
Fires when header toolbar items are clicked:
import { ChatUIComponent, ToolbarSettingsModel, ToolbarItemClickedEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const handleHeaderToolbarClick = (args: ToolbarItemClickedEventArgs) => {
if (args.item.iconCss === 'e-icons e-menu') {
console.log('Menu clicked');
}
else if (args.item.iconCss === 'e-icons e-refresh') {
console.log('Refresh clicked');
}
};
const headerToolbar: ToolbarSettingsModel = {
items: [
{ iconCss: 'e-icons e-refresh', align: 'Right', tooltip: 'Refresh' },
{ iconCss: 'e-icons e-menu', align: 'Right', tooltip: 'Menu' }
],
itemClicked: handleHeaderToolbarClick
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
headerToolbar={headerToolbar}
/>
);
}Advanced Event Handling
Combined Event Handler
function App() {
const chatRef = useRef<ChatUIComponent>(null);
const eventLog: string[] = [];
const logEvent = (eventName: string, details: any) => {
const timestamp = new Date().toLocaleTimeString();
eventLog.push(`[${timestamp}] ${eventName}: ${JSON.stringify(details)}`);
console.log(eventLog[eventLog.length - 1]);
};
return (
<ChatUIComponent
ref={chatRef}
user={{ id: "user1", user: "Albert" }}
created={() => logEvent('Created', 'Chat initialized')}
messageSend={(args) => logEvent('MessageSend', args.message.text)}
userTyping={() => logEvent('UserTyping', 'User typing...')}
/>
);
}Best Practices
1. Use event validation to prevent invalid data 2. Handle cancellations gracefully with user feedback 3. Implement error handling for attachment events 4. Log important events for debugging and analytics 5. Debounce typing events to improve performance 6. Validate file uploads before processing
File Attachments
Table of Contents
- Enable Attachments
- Configure Upload Endpoints
- File Type Restrictions
- File Size Restrictions
- Attachment Templates
- Attachment Click Event
- Drag and Drop
Enable Attachments
Basic Attachment Support
Enable file attachment functionality:
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableAttachments={true}
/>
);
}Configure Upload Endpoints
Set Save and Remove URLs
Configure server endpoints for file operations:
import { ChatUIComponent, FileAttachmentSettingsModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove'
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableAttachments={true}
attachmentSettings={attachmentSettings}
/>
);
}Custom Server Implementation
// Example backend endpoint for Node.js/Express
app.post('/api/upload', (req, res) => {
// Handle file upload
const file = req.files.upload;
const uploadPath = path.join(__dirname, 'uploads', file.name);
file.mv(uploadPath, (err) => {
if (err) {
res.status(500).json({ error: 'Upload failed' });
} else {
res.json({ name: file.name, size: file.size, url: `/uploads/${file.name}` });
}
});
});
app.post('/api/remove', (req, res) => {
// Handle file removal
const fileName = req.body.fileName;
const filePath = path.join(__dirname, 'uploads', fileName);
fs.unlink(filePath, (err) => {
if (err) {
res.status(500).json({ error: 'Delete failed' });
} else {
res.json({ success: true });
}
});
});File Type Restrictions
Allowed File Types
Restrict uploads to specific file types:
import { ChatUIComponent, FileAttachmentSettingsModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
allowedFileTypes: '.pdf, .docx, .xlsx, .txt, .jpg, .png'
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableAttachments={true}
attachmentSettings={attachmentSettings}
/>
);
}Images Only
const attachmentSettings: FileAttachmentSettingsModel = {
allowedFileTypes: '.jpg, .jpeg, .png, .gif, .webp'
};Documents Only
const attachmentSettings: FileAttachmentSettingsModel = {
allowedFileTypes: '.pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx'
};File Size Restrictions
Maximum File Size
Limit upload file size (in bytes):
import { ChatUIComponent, FileAttachmentSettingsModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
maxFileSize: 5242880 // 5MB
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableAttachments={true}
attachmentSettings={attachmentSettings}
/>
);
}Common Size Limits
// 1MB
maxFileSize: 1048576
// 10MB
maxFileSize: 10485760
// 50MB
maxFileSize: 52428800
// 100MB
maxFileSize: 104857600Maximum File Count
Limit number of files per message:
const attachmentSettings: FileAttachmentSettingsModel = {
maximumCount: 5 // Maximum 5 files per upload
};Attachment Templates
Preview Template
Customize how attachments appear before upload:
import { ChatUIComponent, FileAttachmentSettingsModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const PreviewTemplate = (props: { selectedFile: any }) => {
const file = props.selectedFile;
const isImage = file.type?.startsWith('image/');
return (
<div style={{
border: '1px solid #ddd',
borderRadius: '8px',
padding: '12px',
marginBottom: '8px'
}}>
<div style={{ fontWeight: 'bold', marginBottom: '8px' }}>
{file.name}
</div>
{isImage && (
<img
src={file.fileSource}
alt={file.name}
style={{ maxWidth: '200px', maxHeight: '200px' }}
/>
)}
<div style={{ fontSize: '12px', color: '#999', marginTop: '8px' }}>
Size: {(file.size / 1024).toFixed(2)} KB
</div>
</div>
);
};
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
previewTemplate: PreviewTemplate
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableAttachments={true}
attachmentSettings={attachmentSettings}
/>
);
}Attachment Display Template
Customize how attachments appear in messages:
const AttachmentTemplate = (props: { selectedFile: any }) => {
const file = props.selectedFile;
const isImage = file.type?.startsWith('image/');
const isVideo = file.type?.startsWith('video/');
return (
<div style={{
display: 'flex',
alignItems: 'center',
gap: '12px',
padding: '8px',
backgroundColor: '#f5f5f5',
borderRadius: '8px'
}}>
{isImage && (
<img src={file.fileSource} alt={file.name} style={{
width: '40px',
height: '40px',
borderRadius: '4px'
}} />
)}
{isVideo && (
<span className="e-icons e-video" style={{ fontSize: '24px' }}></span>
)}
{!isImage && !isVideo && (
<span className="e-icons e-file" style={{ fontSize: '24px' }}></span>
)}
<div style={{ flex: 1 }}>
<div style={{ fontWeight: 'bold', fontSize: '14px' }}>
{file.name}
</div>
<div style={{ fontSize: '12px', color: '#999' }}>
{(file.size / 1024).toFixed(2)} KB
</div>
</div>
</div>
);
};Footer Attachment Template
Customize how attachments appear in the footer area before sending:
import { ChatUIComponent, FileAttachmentSettingsModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const FooterAttachmentTemplate = (props: { selectedFiles: any[] }) => {
return (
<div style={{ padding: '8px' }}>
<div style={{ fontSize: '12px', color: '#666', marginBottom: '8px' }}>
Selected Files ({props.selectedFiles.length}):
</div>
{props.selectedFiles.map((file, index) => (
<div key={index} style={{
display: 'flex',
alignItems: 'center',
gap: '8px',
padding: '6px',
backgroundColor: '#f5f5f5',
borderRadius: '6px',
marginBottom: '6px'
}}>
<span className="e-icons e-file" style={{ fontSize: '18px' }}></span>
<div style={{ flex: 1 }}>
<div style={{ fontSize: '13px', fontWeight: '500' }}>
{file.name}
</div>
<div style={{ fontSize: '11px', color: '#999' }}>
{(file.size / 1024).toFixed(1)} KB
</div>
</div>
<button
style={{
background: 'transparent',
border: 'none',
cursor: 'pointer',
fontSize: '16px'
}}
>
<span className="e-icons e-close"></span>
</button>
</div>
))}
</div>
);
};
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
attachmentTemplate: FooterAttachmentTemplate // Custom footer template
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableAttachments={true}
attachmentSettings={attachmentSettings}
/>
);
}Attachment Click Event
Handle Attachment Clicks
Respond when user clicks on an attachment (either before sending or after sent):
import { ChatUIComponent, FileAttachmentSettingsModel, ChatAttachmentClickEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const handleAttachmentClick = (args: ChatAttachmentClickEventArgs) => {
console.log('Attachment clicked:', args.file.name);
console.log('File type:', args.file.type);
console.log('File size:', args.file.size);
// Open file in new tab
if (args.file.url) {
window.open(args.file.url, '_blank');
}
// Cancel default behavior if needed
// args.cancel = true;
};
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
attachmentClick: handleAttachmentClick
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableAttachments={true}
attachmentSettings={attachmentSettings}
/>
);
}Preview Image Attachments
const handleAttachmentClick = (args: ChatAttachmentClickEventArgs) => {
const file = args.file;
// Check if it's an image
if (file.type?.startsWith('image/')) {
// Show image in modal/preview
showImagePreview(file.url || file.fileSource);
args.cancel = true; // Prevent default action
} else if (file.type === 'application/pdf') {
// Open PDF in new tab
window.open(file.url, '_blank');
args.cancel = true;
}
// For other files, use default behavior (download)
};
const showImagePreview = (imageUrl: string) => {
// Implementation for image preview modal
const modal = document.createElement('div');
modal.innerHTML = `
<div style="position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.8); display: flex; align-items: center;
justify-content: center; z-index: 9999;">
<img src="${imageUrl}" style="max-width: 90%; max-height: 90%;" />
</div>
`;
modal.onclick = () => modal.remove();
document.body.appendChild(modal);
};Download Attachment
const handleAttachmentClick = (args: ChatAttachmentClickEventArgs) => {
const file = args.file;
// Trigger download
const link = document.createElement('a');
link.href = file.url || file.fileSource;
link.download = file.name;
link.click();
// Cancel default behavior
args.cancel = true;
console.log('Downloading:', file.name);
};Event Arguments Details
The ChatAttachmentClickEventArgs provides:
| Property | Type | Description |
|---|---|---|
cancel | boolean | Set to true to prevent default preview rendering |
event | Event | The underlying click event object |
file | FileInfo | Complete file information (name, type, size, url) |
name | string | Name of the event ('attachmentClick') |
Complete Attachment Click Example
import { ChatUIComponent, FileAttachmentSettingsModel, ChatAttachmentClickEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import React, { useState } from 'react';
function App() {
const [previewFile, setPreviewFile] = useState<any>(null);
const handleAttachmentClick = (args: ChatAttachmentClickEventArgs) => {
const file = args.file;
console.log('Attachment clicked:', {
name: file.name,
type: file.type,
size: file.size,
url: file.url
});
// Handle different file types
if (file.type?.startsWith('image/')) {
// Show image preview
setPreviewFile(file);
args.cancel = true;
} else if (file.type === 'application/pdf') {
// Open PDF in new window
window.open(file.url, '_blank');
args.cancel = true;
} else if (file.type?.startsWith('video/')) {
// Show video player
setPreviewFile(file);
args.cancel = true;
}
// For documents, allow default download behavior
};
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
allowedFileTypes: '.jpg, .png, .pdf, .mp4, .doc, .docx',
maxFileSize: 10485760, // 10MB
attachmentClick: handleAttachmentClick
};
return (
<div>
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableAttachments={true}
attachmentSettings={attachmentSettings}
/>
{/* Preview Modal */}
{previewFile && (
<div
style={{
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
background: 'rgba(0,0,0,0.9)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 9999
}}
onClick={() => setPreviewFile(null)}
>
{previewFile.type?.startsWith('image/') && (
<img
src={previewFile.url || previewFile.fileSource}
alt={previewFile.name}
style={{ maxWidth: '90%', maxHeight: '90%' }}
/>
)}
{previewFile.type?.startsWith('video/') && (
<video
src={previewFile.url || previewFile.fileSource}
controls
style={{ maxWidth: '90%', maxHeight: '90%' }}
/>
)}
</div>
)}
</div>
);
}Drag and Drop
Enable Drag-and-Drop
Allow users to drag files directly into chat:
import { ChatUIComponent, FileAttachmentSettingsModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
enableDragAndDrop: true // Enable drag and drop
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableAttachments={true}
attachmentSettings={attachmentSettings}
/>
);
}Save Format
Configure File Format
Choose how files are sent to the server:
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
saveFormat: 'Base64' // Use Base64 encoding
};Options:
'Blob'- Binary large object (default, for fast uploads)'Base64'- Base64 encoded string (for text-based transmission)
Server Path Configuration
Specify Upload Directory
Set the server path where files are stored:
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
path: '/uploads/chat-files'
};Complete Attachment Example
Full Configuration
import { ChatUIComponent, FileAttachmentSettingsModel, BeforeAttachmentUploadEventArgs, AttachmentUploadSuccessEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const handleBeforeUpload = (args: BeforeAttachmentUploadEventArgs) => {
console.log('Uploading files:', args.files);
};
const handleUploadSuccess = (args: AttachmentUploadSuccessEventArgs) => {
console.log('File uploaded:', args.fileName);
};
const handleUploadFailure = (args) => {
console.error('Upload failed:', args.error);
};
const attachmentSettings: FileAttachmentSettingsModel = {
saveUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
allowedFileTypes: '.pdf, .docx, .xlsx, .jpg, .png, .gif',
maxFileSize: 5242880, // 5MB
maximumCount: 5,
enableDragAndDrop: true,
beforeAttachmentUpload: handleBeforeUpload,
attachmentUploadSuccess: handleUploadSuccess,
attachmentUploadFailure: handleUploadFailure
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
enableAttachments={true}
attachmentSettings={attachmentSettings}
/>
);
}Best Practices
1. Validate file types on both client and server 2. Implement file size limits to prevent abuse 3. Scan uploaded files for malware 4. Store files securely with proper permissions 5. Clean up old files periodically 6. Provide clear feedback for upload progress 7. Handle network failures gracefully
Getting Started with Chat-UI
Table of Contents
Installation
Install via npm
npm install @syncfusion/ej2-react-interactive-chat --saveOr using yarn:
yarn add @syncfusion/ej2-react-interactive-chatCSS Configuration
Import Required Stylesheets
Add the following imports to your src/App.css or main CSS file:
/* Import base theme styles */
@import "../node_modules/@syncfusion/ej2-base/styles/material.css";
/* Import component-specific styles */
@import "../node_modules/@syncfusion/ej2-inputs/styles/material.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material.css";
/* Import Chat-UI styles */
@import "../node_modules/@syncfusion/ej2-react-interactive-chat/styles/material.css";Available Themes
Choose one of these themes to import:
material.css- Material Design (recommended)bootstrap5.css- Bootstrap 5 stylingfluent.css- Microsoft Fluent Designtailwind.css- Tailwind CSS stylingbootstrap.css- Bootstrap 4 stylinghighcontrast.css- High contrast accessibility theme
Basic Implementation
Minimal Chat Component
import { ChatUIComponent, UserModel } from '@syncfusion/ej2-react-interactive-chat';
import * as React from 'react';
function App() {
const currentUser: UserModel = {
id: "user1",
user: "Albert"
};
return (
<ChatUIComponent user={currentUser} id="chat-ui" />
);
}
export default App;With Messages
import { ChatUIComponent, MessagesDirective, MessageDirective, UserModel } from '@syncfusion/ej2-react-interactive-chat';
import * as React from 'react';
function App() {
const currentUser: UserModel = {
id: "user1",
user: "Albert"
};
const otherUser: UserModel = {
id: "user2",
user: "Michale Suyama"
};
return (
<ChatUIComponent user={currentUser} headerText="Chat">
<MessagesDirective>
<MessageDirective
text="Hi, how are you?"
author={currentUser}
/>
<MessageDirective
text="I'm good, thanks for asking!"
author={otherUser}
/>
</MessagesDirective>
</ChatUIComponent>
);
}
export default App;TypeScript Configuration
For TypeScript projects, create types for your user and message models:
import { ChatUIComponent, MessagesDirective, MessageDirective, UserModel, MessageModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
interface AppUser extends UserModel {
department?: string;
}
interface AppMessage extends MessageModel {
priority?: 'low' | 'medium' | 'high';
}
function App() {
const currentUser: AppUser = {
id: "user1",
user: "Albert",
department: "Support"
};
const messages: AppMessage[] = [
{
text: "Hello!",
author: currentUser,
priority: 'high'
}
];
return (
<ChatUIComponent user={currentUser}>
<MessagesDirective>
{messages.map((msg, index) => (
<MessageDirective key={index} {...msg} />
))}
</MessagesDirective>
</ChatUIComponent>
);
}
export default App;First Message Example
Complete Working Example
import { ChatUIComponent, MessagesDirective, MessageDirective, UserModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
import ReactDOM from 'react-dom';
import '../styles/index.css';
function App() {
// Define current user
const currentUser: UserModel = {
id: "user1",
user: "Albert",
avatarBgColor: "#0c888e"
};
// Define other user in conversation
const supportAgent: UserModel = {
id: "user2",
user: "Michale Suyama",
avatarUrl: 'https://ej2.syncfusion.com/demos/src/avatar/images/pic03.png'
};
return (
<div style={{ height: '600px', width: '100%' }}>
<ChatUIComponent
user={currentUser}
headerText="Support Chat"
placeholder="Type your message..."
showHeader={true}
showFooter={true}
>
<MessagesDirective>
<MessageDirective
text="Welcome! How can I help you today?"
author={supportAgent}
timeStamp={new Date('2024-01-15 09:00')}
/>
<MessageDirective
text="Hi, I need help with my account."
author={currentUser}
timeStamp={new Date('2024-01-15 09:05')}
/>
<MessageDirective
text="I'd be happy to assist! What's the issue?"
author={supportAgent}
timeStamp={new Date('2024-01-15 09:06')}
/>
</MessagesDirective>
</ChatUIComponent>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));Globalization and RTL
Table of Contents
Localization
Set Language/Locale
Configure Chat-UI for different languages:
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
return (
<>
{/* English (default) */}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
locale="en"
/>
{/* German */}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
locale="de"
/>
{/* Arabic */}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
locale="ar"
/>
{/* Spanish */}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
locale="es"
/>
</>
);
}Supported Languages
| Locale | Language | Code |
|---|---|---|
| English | English | en |
| German | Deutsch | de |
| Spanish | Español | es |
| French | Français | fr |
| Arabic | العربية | ar |
| Portuguese | Português | pt |
| Russian | Русский | ru |
| Chinese Simplified | 简体中文 | zh |
| Japanese | 日本語 | ja |
RTL Support
Enable Right-to-Left Layout
Enable RTL for languages like Arabic and Hebrew:
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
return (
<>
{/* Arabic with RTL */}
<ChatUIComponent
user={{ id: "user1", user: "أحمد" }}
locale="ar"
enableRtl={true}
/>
{/* Hebrew with RTL */}
<ChatUIComponent
user={{ id: "user1", user: "דויד" }}
locale="he"
enableRtl={true}
/>
{/* Persian with RTL */}
<ChatUIComponent
user={{ id: "user1", user: "علی" }}
locale="fa"
enableRtl={true}
/>
</>
);
}RTL Styling
/* RTL layout adjustments */
.e-chat-ui[dir="rtl"] {
direction: rtl;
text-align: right;
}
.e-chat-ui[dir="rtl"] .e-input-group {
direction: rtl;
}
.e-chat-ui[dir="rtl"] .e-send-button {
margin-left: 0;
margin-right: 8px;
}Locale Strings
Typing Indicator Text
The typing indicator uses localized strings based on user count:
| Key | Default (English) | Count |
|---|---|---|
oneUserTyping | {0} is typing | 1 user |
twoUserTyping | {0} and {1} are typing | 2 users |
threeUserTyping | {0}, {1}, and {2} other are typing | 3+ users (shows 2 + count) |
multipleUsersTyping | {0}, {1}, and {2} others are typing | 3+ users |
Typing Examples by Locale
// English
"{0} is typing" → "Albert is typing"
"{0} and {1} are typing" → "Albert and Michale are typing"
// German
"{0} schreibt" → "Albert schreibt"
"{0} und {1} schreiben" → "Albert und Michale schreiben"
// Arabic (RTL)
"يكتب {0}" → "يكتب Albert"
"يكتب {0} و {1}" → "يكتب Albert و Michale"Custom Localization
Add Custom Locale
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
// Define custom locale
const customLocale = {
'oneUserTyping': '{0} est en train d\'écrire',
'twoUserTyping': '{0} et {1} écrivent',
'threeUserTyping': '{0}, {1}, et {2} autre écrit',
'multipleUsersTyping': '{0}, {1}, et {2} autres écrivent'
};
function App() {
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
locale="fr" // French locale
/>
);
}Complete Example
Multi-Language Chat Application
import { ChatUIComponent, MessagesDirective, MessageDirective, UserModel } from '@syncfusion/ej2-react-interactive-chat';
import React, { useState } from 'react';
function App() {
const [selectedLocale, setSelectedLocale] = useState('en');
const [enableRtl, setEnableRtl] = useState(false);
const currentUser: UserModel = {
id: "user1",
user: selectedLocale === 'ar' ? 'أحمد' : 'Albert'
};
const typingUsers = selectedLocale === 'ar'
? [{ id: "user2", user: 'محمد' }]
: [{ id: "user2", user: 'Support' }];
const localeMessages = {
en: {
headerText: 'Support Chat',
placeholder: 'Type your message...'
},
de: {
headerText: 'Support-Chat',
placeholder: 'Geben Sie Ihre Nachricht ein...'
},
ar: {
headerText: 'دردشة الدعم',
placeholder: 'اكتب رسالتك...'
},
es: {
headerText: 'Chat de Soporte',
placeholder: 'Escribe tu mensaje...'
}
};
const messages = localeMessages[selectedLocale] || localeMessages.en;
return (
<div style={{ padding: '20px' }}>
{/* Language Selector */}
<div style={{ marginBottom: '20px' }}>
<label>
Language:
<select
value={selectedLocale}
onChange={(e) => {
setSelectedLocale(e.target.value);
setEnableRtl(e.target.value === 'ar');
}}
>
<option value="en">English</option>
<option value="de">Deutsch</option>
<option value="es">Español</option>
<option value="ar">العربية</option>
</select>
</label>
</div>
{/* Chat Component */}
<ChatUIComponent
user={currentUser}
headerText={messages.headerText}
placeholder={messages.placeholder}
locale={selectedLocale}
enableRtl={enableRtl}
typingUsers={typingUsers}
height="500px"
>
<MessagesDirective>
<MessageDirective
text={selectedLocale === 'ar' ? 'مرحبا بك في الدعم!' : 'Welcome to support!'}
author={{ id: "user2", user: selectedLocale === 'ar' ? 'الدعم' : 'Support' }}
/>
</MessagesDirective>
</ChatUIComponent>
</div>
);
}
export default App;Language Detection
Auto-Detect User Language
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React, { useState, useEffect } from 'react';
function App() {
const [locale, setLocale] = useState('en');
const [rtlEnabled, setRtlEnabled] = useState(false);
useEffect(() => {
// Get browser language
const browserLang = navigator.language.split('-')[0];
// Map to supported locales
const supportedLocales = {
'en': 'en',
'de': 'de',
'es': 'es',
'ar': 'ar',
'fr': 'fr',
'pt': 'pt'
};
const detectedLocale = supportedLocales[browserLang] || 'en';
setLocale(detectedLocale);
// Enable RTL for specific locales
const rtlLocales = ['ar', 'he', 'fa'];
setRtlEnabled(rtlLocales.includes(detectedLocale));
}, []);
return (
<ChatUIComponent
user={{ id: "user1", user: "User" }}
locale={locale}
enableRtl={rtlEnabled}
/>
);
}Responsive RTL Layout
CSS for RTL Responsiveness
/* Base styles */
.e-chat-ui {
direction: ltr;
}
/* RTL adjustments */
.e-chat-ui[dir="rtl"] {
direction: rtl;
}
.e-chat-ui[dir="rtl"] .e-chat-header {
padding-right: 16px;
padding-left: 8px;
}
.e-chat-ui[dir="rtl"] .e-message-right {
margin-right: 0;
margin-left: auto;
}
.e-chat-ui[dir="rtl"] .e-message-left {
margin-left: 0;
margin-right: auto;
}
.e-chat-ui[dir="rtl"] .e-send-button {
order: -1;
}
/* Mobile adjustments */
@media (max-width: 480px) {
.e-chat-ui[dir="rtl"] {
font-size: 14px;
}
.e-chat-ui[dir="rtl"] .e-message-bubble {
max-width: 85%;
}
}Best Practices
1. Detect user language from browser settings 2. Support RTL automatically for RTL locales 3. Test with real native speakers when localizing 4. Keep text labels concise for different languages 5. Handle text expansion (RTL text often needs more space) 6. Use Unicode properly for special characters 7. Test on mobile for RTL layouts 8. Provide language selector for user preference
Supported RTL Languages
- Arabic (ar)
- Hebrew (he)
- Persian/Farsi (fa)
- Urdu (ur)
Header Customization
Table of Contents
- Header Visibility
- Header Text and Icon
- Toolbar Configuration
- Toolbar Item Types
- Toolbar Item Properties
- Toolbar Click Events
Header Visibility
Show or Hide Header
Control whether the header section is displayed:
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
return (
<>
{/* Header visible (default) */}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
showHeader={true}
/>
{/* Header hidden */}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
showHeader={false}
/>
</>
);
}Header Text and Icon
Set Header Title
Display a title in the header:
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
headerText="Customer Support"
/>
);
}Set Header Icon
Add an icon to the header:
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
headerText="Support Team"
headerIconCss="e-icons e-people"
/>
);
}Available Header Icons
Common icon classes:
e-people- Group/teame-person- Single usere-chat- Chat bubblee-phone- Phonee-mail- Emaile-info- Informatione-home- Home
Toolbar Configuration
Basic Toolbar Setup
Add toolbar items to the header:
import { ChatUIComponent, ToolbarSettingsModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const headerToolbar: ToolbarSettingsModel = {
items: [
{ iconCss: 'e-icons e-refresh', align: 'Right', tooltip: 'Refresh' },
{ iconCss: 'e-icons e-menu', align: 'Right', tooltip: 'Menu' }
]
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
headerToolbar={headerToolbar}
/>
);
}Toolbar Item Types
Button Items
Regular clickable buttons:
const headerToolbar: ToolbarSettingsModel = {
items: [
{ type: 'Button', iconCss: 'e-icons e-refresh', tooltip: 'Refresh' },
{ type: 'Button', text: 'Help', align: 'Right' }
]
};Separator
Visual separator between toolbar items:
const headerToolbar: ToolbarSettingsModel = {
items: [
{ iconCss: 'e-icons e-search' },
{ type: 'Separator' },
{ iconCss: 'e-icons e-user', align: 'Right' }
]
};Input Items
For custom input elements:
const headerToolbar: ToolbarSettingsModel = {
items: [
{ type: 'Input', template: '<input type="text" placeholder="Search..." />' }
]
};Toolbar Item Properties
Icon CSS
Set icon styling:
const item = {
iconCss: 'e-icons e-menu'
};Text
Display text label:
const item = {
text: 'Options',
align: 'Right'
};Tooltip
Show tooltip on hover:
const item = {
iconCss: 'e-icons e-refresh',
tooltip: 'Refresh conversation',
align: 'Right'
};Alignment
Position item in toolbar:
// Left alignment (default)
{ iconCss: 'e-icons e-search', align: 'Left' }
// Center alignment
{ iconCss: 'e-icons e-info', align: 'Center' }
// Right alignment
{ iconCss: 'e-icons e-user', align: 'Right' }Visibility
Show or hide toolbar item:
const item = {
iconCss: 'e-icons e-menu',
visible: true // or false
};Disabled State
Disable toolbar item:
const item = {
iconCss: 'e-icons e-refresh',
disabled: false // or true
};CSS Class
Apply custom styling:
const item = {
iconCss: 'e-icons e-user',
cssClass: 'custom-toolbar-item'
};Tab Index
Enable keyboard navigation:
const item = {
text: 'Settings',
tabIndex: 1
};Toolbar Click Events
Handle Toolbar Item Click
import { ChatUIComponent, ToolbarSettingsModel, ToolbarItemClickedEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const handleToolbarClick = (args: ToolbarItemClickedEventArgs) => {
console.log('Toolbar item clicked:', args.item);
if (args.item.iconCss === 'e-icons e-refresh') {
console.log('Refresh clicked');
// Reload conversation
} else if (args.item.text === 'Close') {
console.log('Close clicked');
// Close chat
}
};
const headerToolbar: ToolbarSettingsModel = {
items: [
{ iconCss: 'e-icons e-refresh', align: 'Right', tooltip: 'Refresh' },
{ text: 'Close', align: 'Right' }
],
itemClicked: handleToolbarClick
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
headerToolbar={headerToolbar}
/>
);
}Complete Header Example
Full Customization
import { ChatUIComponent, ToolbarSettingsModel, ToolbarItemClickedEventArgs, MessagesDirective, MessageDirective } from '@syncfusion/ej2-react-interactive-chat';
import React, { useRef, useState } from 'react';
function App() {
const chatRef = useRef(null);
const [isRefreshing, setIsRefreshing] = useState(false);
const handleToolbarClick = (args: ToolbarItemClickedEventArgs) => {
if (args.item.iconCss === 'e-icons e-refresh') {
setIsRefreshing(true);
setTimeout(() => setIsRefreshing(false), 1000);
console.log('Refreshing...');
}
else if (args.item.text === 'Info') {
alert('Syncfusion Chat Support');
}
else if (args.item.text === 'Settings') {
console.log('Open settings');
}
};
const headerToolbar: ToolbarSettingsModel = {
items: [
{ text: 'Info', align: 'Right', tooltip: 'Chat information' },
{ type: 'Separator' },
{ text: 'Settings', align: 'Right', tooltip: 'Settings' },
{ iconCss: 'e-icons e-refresh', align: 'Right', tooltip: 'Refresh' }
],
itemClicked: handleToolbarClick
};
return (
<ChatUIComponent
ref={chatRef}
user={{ id: "user1", user: "Albert" }}
headerText="Support Chat"
headerIconCss="e-icons e-chat"
showHeader={true}
headerToolbar={headerToolbar}
>
<MessagesDirective>
<MessageDirective
text="Welcome to support!"
author={{ id: "agent", user: "Support" }}
/>
</MessagesDirective>
</ChatUIComponent>
);
}
export default App;Custom CSS for Toolbar
/* Style toolbar items */
.custom-toolbar-item {
font-weight: 500;
}
.custom-toolbar-item:hover {
background-color: #f0f0f0;
cursor: pointer;
}
.custom-toolbar-item.disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Style specific icons */
.e-icons.e-refresh {
transition: transform 0.3s ease;
}
.e-icons.e-refresh:hover {
transform: rotate(180deg);
}Dynamic Toolbar Management
Enable/Disable Toolbar Items
function App() {
const [toolbarConfig, setToolbarConfig] = useState<ToolbarSettingsModel>({
items: [
{ iconCss: 'e-icons e-refresh', disabled: false },
{ text: 'Archive', disabled: false }
]
});
const toggleItemDisabled = (index: number) => {
const newItems = [...toolbarConfig.items];
newItems[index].disabled = !newItems[index].disabled;
setToolbarConfig({ ...toolbarConfig, items: newItems });
};
return (
<>
<button onClick={() => toggleItemDisabled(0)}>
Toggle Refresh
</button>
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
headerToolbar={toolbarConfig}
/>
</>
);
}Best Practices
1. Use intuitive icons for toolbar items 2. Provide tooltips for clarity 3. Group related items together 4. Use separator to organize toolbar logically 5. Disable items when not available 6. Align important items to the right 7. Test keyboard navigation with tab keys
Mention Integration
Table of Contents
Configure Mention Users
Setup Mention Users
Define users available for mentioning:
import { ChatUIComponent, UserModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const currentUser: UserModel = {
id: "user1",
user: "Albert"
};
const mentionUsers: UserModel[] = [
{ id: "user2", user: "Michale Suyama" },
{ id: "user3", user: "Reena" },
{ id: "user4", user: "John Smith" }
];
return (
<ChatUIComponent
user={currentUser}
mentionUsers={mentionUsers}
/>
);
}Mention Trigger
When users type @, a dropdown appears with mention options:
// User types: "@M"
// Dropdown shows:
// - Michale Suyama
// - (filtered by "M")Customize Trigger Character
Change Trigger Character
Use different trigger character instead of @:
import { ChatUIComponent, UserModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const mentionUsers: UserModel[] = [
{ id: "user2", user: "Michale" },
{ id: "user3", user: "Reena" }
];
return (
<>
{/* Default: @ trigger */}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
mentionUsers={mentionUsers}
mentionTriggerChar="@" // Default
/>
{/* Alternative triggers */}
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
mentionUsers={mentionUsers}
mentionTriggerChar="#" // Hash tag
/>
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
mentionUsers={mentionUsers}
mentionTriggerChar="+" // Plus sign
/>
</>
);
}Predefined Mentions
Include Mentions in Messages
Use placeholder syntax to include predefined mentions:
import { ChatUIComponent, MessagesDirective, MessageDirective, UserModel, MessageModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const currentUser: UserModel = {
id: "user1",
user: "Albert"
};
const michale: UserModel = {
id: "user2",
user: "Michale"
};
const reena: UserModel = {
id: "user3",
user: "Reena"
};
const messages: MessageModel[] = [
{
text: "Hi {0}, can you review this?",
author: currentUser,
mentionUsers: [michale] // {0} = Michale
},
{
text: "Sure {0}, I'll check it out.",
author: michale,
mentionUsers: [currentUser] // {0} = Albert
},
{
text: "{0} and {1}, please join the call.",
author: currentUser,
mentionUsers: [michale, reena] // {0} = Michale, {1} = Reena
}
];
return (
<ChatUIComponent user={currentUser} messages={messages} />
);
}Placeholder Mapping
// Single mention
text: "Hey {0}!"
mentionUsers: [user1]
// Result: "Hey User1!"
// Multiple mentions
text: "{0} and {1} are here"
mentionUsers: [user1, user2]
// Result: "User1 and User2 are here"
// Three mentions
text: "{0}, {1}, and {2} should see this"
mentionUsers: [user1, user2, user3]
// Result: "User1, User2, and User3 should see this"Mention Selection Event
Handle Mention Selection
Respond when user selects a mention:
import { ChatUIComponent, MentionSelectEventArgs, UserModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const mentionUsers: UserModel[] = [
{ id: "user2", user: "Michale Suyama" },
{ id: "user3", user: "Reena" }
];
const handleMentionSelect = (args: MentionSelectEventArgs) => {
console.log('Mention selected:', args.user.user);
console.log('User ID:', args.user.id);
// args.user contains the selected user details
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
mentionUsers={mentionUsers}
mentionSelect={handleMentionSelect}
/>
);
}Log Mention Selection
const handleMentionSelect = (args: MentionSelectEventArgs) => {
const selectedUser = args.user;
console.log(`User mentioned: ${selectedUser.user}`);
console.log(`User ID: ${selectedUser.id}`);
// Send analytics
trackEvent('mention_selected', {
mentionedUser: selectedUser.user,
timestamp: new Date()
});
};Complete Example
Full Mention Setup
import { ChatUIComponent, MessagesDirective, MessageDirective, UserModel, MessageModel, MentionSelectEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const currentUser: UserModel = {
id: "user1",
user: "Albert"
};
const mentionUsers: UserModel[] = [
{ id: "user2", user: "Michale Suyama", avatarBgColor: "#e3165b" },
{ id: "user3", user: "Reena", avatarBgColor: "#6d3e94" },
{ id: "user4", user: "John Smith", avatarBgColor: "#3c78d8" },
{ id: "user5", user: "Sarah Johnson", avatarBgColor: "#f5a623" }
];
const initialMessages: MessageModel[] = [
{
text: "Hey team, let's sync up!",
author: currentUser
},
{
text: "{0}, can you prepare the report?",
author: { id: "user2", user: "Manager" },
mentionUsers: [mentionUsers[0]] // Michale
},
{
text: "Sure, {0} and {1} - I'll have it by tomorrow.",
author: currentUser,
mentionUsers: [mentionUsers[0], mentionUsers[1]] // Michale and Reena
}
];
const handleMentionSelect = (args: MentionSelectEventArgs) => {
console.log(`@${args.user.user} mentioned`);
};
return (
<ChatUIComponent
user={currentUser}
headerText="Team Chat"
mentionUsers={mentionUsers}
mentionTriggerChar="@"
mentionSelect={handleMentionSelect}
messages={initialMessages}
>
<MessagesDirective>
{initialMessages.map((msg, index) => (
<MessageDirective
key={index}
text={msg.text}
author={msg.author}
mentionUsers={msg.mentionUsers}
/>
))}
</MessagesDirective>
</ChatUIComponent>
);
}
export default App;Mention Display
How Mentions Appear
In the chat interface:
- Typed mentions:
@Michale Suyama→ clickable user pill - Placeholder mentions:
{0}→ replaced with user name - Color coded: Different users get different colors
- Hoverable: Shows user details on hover
Mention Styling
Users are typically displayed as:
- Pills/badges with user color
- Clickable to open user profile
- Highlighted with special formatting
Best Practices
1. Limit mention users to relevant people (5-50) 2. Alphabetically sort mention list 3. Show avatars in mention dropdown 4. Handle large teams with search capability 5. Validate mentions before sending 6. Log mention analytics for engagement 7. Notify mentioned users appropriately
Advanced: Dynamic Mention Users
import React, { useState, useEffect } from 'react';
import { ChatUIComponent, UserModel } from '@syncfusion/ej2-react-interactive-chat';
function App() {
const [mentionUsers, setMentionUsers] = useState<UserModel[]>([]);
useEffect(() => {
// Fetch team members from API
fetchTeamMembers().then(members => {
setMentionUsers(members);
});
}, []);
const fetchTeamMembers = async (): Promise<UserModel[]> => {
// API call to get team members
const response = await fetch('/api/team/members');
const data = await response.json();
return data.map(member => ({
id: member.id,
user: member.name
}));
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
mentionUsers={mentionUsers}
/>
);
}Message Management
Table of Contents
- Basic Message Configuration
- Adding Messages Dynamically
- Updating Messages
- Auto Scroll to Bottom
- Load Messages On Demand
- Pinned Messages
- Message Replies
- Forwarding Messages
- Message Status
- Message Toolbar
- Markdown Content
Basic Message Configuration
Setting Message Text
The text property defines the message content:
import { ChatUIComponent, MessagesDirective, MessageDirective, UserModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const user: UserModel = { id: "user1", user: "Albert" };
const agent: UserModel = { id: "user2", user: "Support" };
return (
<ChatUIComponent user={user}>
<MessagesDirective>
<MessageDirective
text="This is a simple message"
author={agent}
/>
</MessagesDirective>
</ChatUIComponent>
);
}Message ID
Assign unique IDs to messages for programmatic access:
<MessagesDirective>
<MessageDirective
id="msg-1"
text="Hello"
author={agent}
/>
<MessageDirective
id="msg-2"
text="Hi there"
author={user}
/>
</MessagesDirective>Adding Messages Dynamically
Add Message as Object
Use addMessage() with a MessageModel object for full control:
import { ChatUIComponent, UserModel, MessageModel } from '@syncfusion/ej2-react-interactive-chat';
import React, { useRef } from 'react';
function App() {
const chatRef = useRef<ChatUIComponent>(null);
const user: UserModel = { id: "user1", user: "Albert" };
const agent: UserModel = { id: "user2", user: "Support" };
const addMessageWithConfig = () => {
const newMessage: MessageModel = {
id: `msg-${Date.now()}`,
author: agent,
text: "Thank you for your message!",
timeStamp: new Date(),
status: {
text: 'Delivered',
iconCss: 'e-icons e-chat-delivered'
}
};
chatRef.current?.addMessage(newMessage);
};
return (
<>
<button onClick={addMessageWithConfig}>Add Message</button>
<ChatUIComponent ref={chatRef} user={user} height="400px" />
</>
);
}Add Message as String
For simple text messages, pass a string:
const addSimpleMessage = () => {
chatRef.current?.addMessage("This is a quick message!");
};Updating Messages
Modify Existing Messages
Use updateMessage() to edit previously sent messages:
import { ChatUIComponent, MessageModel } from '@syncfusion/ej2-react-interactive-chat';
import React, { useRef } from 'react';
function App() {
const chatRef = useRef<ChatUIComponent>(null);
const editMessage = () => {
const updatedMessage: MessageModel = {
text: "Updated message content (edited)",
author: { id: "user1", user: "Albert" }
};
chatRef.current?.updateMessage(updatedMessage, 'msg-1');
};
return (
<>
<button onClick={editMessage}>Edit First Message</button>
<ChatUIComponent ref={chatRef} user={{ id: "user1", user: "Albert" }} />
</>
);
}Auto Scroll to Bottom
Enable Auto-Scrolling
Automatically scroll to the latest message when new messages are added:
import { ChatUIComponent, MessagesDirective, MessageDirective } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const user = { id: "user1", user: "Albert" };
const agent = { id: "user2", user: "Support" };
return (
<ChatUIComponent
user={user}
autoScrollToBottom={true} // Enable auto-scroll
>
<MessagesDirective>
<MessageDirective text="Hello" author={user} />
<MessageDirective text="Hi there!" author={agent} />
</MessagesDirective>
</ChatUIComponent>
);
}Disable Auto-Scrolling
Keep scroll position when new messages arrive:
<ChatUIComponent
user={user}
autoScrollToBottom={false} // Disable auto-scroll (default)
/>Use Case: User Control
Allow users to toggle auto-scroll behavior:
import { ChatUIComponent, MessageModel } from '@syncfusion/ej2-react-interactive-chat';
import React, { useState, useRef } from 'react';
function App() {
const [autoScroll, setAutoScroll] = useState(true);
const chatRef = useRef<ChatUIComponent>(null);
const user = { id: "user1", user: "Albert" };
const addMessage = () => {
const newMessage: MessageModel = {
author: { id: "user2", user: "Support" },
text: "New message arrived!",
timeStamp: new Date()
};
chatRef.current?.addMessage(newMessage);
};
return (
<div>
<div style={{ marginBottom: '10px' }}>
<label>
<input
type="checkbox"
checked={autoScroll}
onChange={(e) => setAutoScroll(e.target.checked)}
/>
Auto-scroll to latest message
</label>
<button onClick={addMessage} style={{ marginLeft: '10px' }}>
Add Message
</button>
</div>
<ChatUIComponent
ref={chatRef}
user={user}
autoScrollToBottom={autoScroll}
height="400px"
/>
</div>
);
}Load Messages On Demand
Enable Progressive Loading
Load older messages as the user scrolls up, improving performance for large conversation histories:
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
loadOnDemand={true} // Enable on-demand loading
/>
);
}How It Works
When loadOnDemand is enabled:
- Initial load shows recent messages only
- Scrolling to the top triggers loading of older messages
- Improves performance for conversations with thousands of messages
- Reduces initial page load time
Use Case: Large Message History
import { ChatUIComponent, MessageModel } from '@syncfusion/ej2-react-interactive-chat';
import React, { useState } from 'react';
function App() {
const [messages, setMessages] = useState<MessageModel[]>([]);
const user = { id: "user1", user: "Albert" };
// Simulate loading more messages when scrolling up
const loadMoreMessages = () => {
// Fetch older messages from server
fetch('/api/messages?before=' + messages[0]?.timeStamp)
.then(res => res.json())
.then(olderMessages => {
setMessages([...olderMessages, ...messages]);
});
};
return (
<ChatUIComponent
user={user}
messages={messages}
loadOnDemand={true}
height="600px"
/>
);
}Best Practices for On-Demand Loading
1. Load in batches - Fetch 20-50 messages at a time 2. Show loading indicator when fetching older messages 3. Cache loaded messages to avoid re-fetching 4. Set appropriate threshold for triggering load 5. Handle edge cases (no more messages to load)
import { ChatUIComponent, MessageModel } from '@syncfusion/ej2-react-interactive-chat';
import React, { useState, useEffect } from 'react';
function App() {
const [messages, setMessages] = useState<MessageModel[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const user = { id: "user1", user: "Albert" };
useEffect(() => {
// Load initial messages
loadInitialMessages();
}, []);
const loadInitialMessages = async () => {
const response = await fetch('/api/messages?limit=20');
const data = await response.json();
setMessages(data.messages);
setHasMore(data.hasMore);
};
const loadOlderMessages = async () => {
if (isLoading || !hasMore) return;
setIsLoading(true);
const oldestMessageTime = messages[0]?.timeStamp;
try {
const response = await fetch(
`/api/messages?before=${oldestMessageTime}&limit=20`
);
const data = await response.json();
setMessages([...data.messages, ...messages]);
setHasMore(data.hasMore);
} catch (error) {
console.error('Failed to load messages:', error);
} finally {
setIsLoading(false);
}
};
return (
<div>
{isLoading && (
<div style={{ textAlign: 'center', padding: '10px' }}>
Loading older messages...
</div>
)}
<ChatUIComponent
user={user}
messages={messages}
loadOnDemand={true}
height="600px"
/>
</div>
);
}Pinned Messages
Pin Important Messages
Set isPinned to highlight critical messages:
<MessagesDirective>
<MessageDirective
text="Regular message"
author={user}
/>
<MessageDirective
text="This is important - DO NOT DELETE"
author={agent}
isPinned={true}
/>
<MessageDirective
text="Another regular message"
author={user}
/>
</MessagesDirective>Pin Message Dynamically
const pinMessage = (messageId: string) => {
const messages = chatRef.current?.messages || [];
const messageToPin = messages.find(m => m.id === messageId);
if (messageToPin) {
messageToPin.isPinned = true;
chatRef.current?.updateMessage(messageToPin, messageId);
}
};Message Replies
Reply to Original Message
Create threaded conversations with the replyTo property:
import { ChatUIComponent, MessageReplyModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const user: UserModel = { id: "user1", user: "Albert" };
const agent: UserModel = { id: "user2", user: "Support" };
const originalMessage: MessageReplyModel = {
user: agent,
text: "How can I help you?",
messageID: "original-1"
};
return (
<ChatUIComponent user={user}>
<MessagesDirective>
<MessageDirective
id="original-1"
text="How can I help you?"
author={agent}
/>
<MessageDirective
text="I need assistance with billing"
author={user}
replyTo={originalMessage}
/>
</MessagesDirective>
</ChatUIComponent>
);
}Forwarding Messages
Forward Message to Another Conversation
Use isForwarded property to indicate forwarded content:
<MessagesDirective>
<MessageDirective
text="Check out this update!"
author={user}
/>
<MessageDirective
text="Check out this update! (forwarded)"
author={agent}
isForwarded={true}
/>
</MessagesDirective>Forward Message Handler
const forwardMessage = (messageId: string) => {
const messages = chatRef.current?.messages || [];
const msgToForward = messages.find(m => m.id === messageId);
if (msgToForward) {
const forwardedMsg: MessageModel = {
...msgToForward,
isForwarded: true,
id: `forwarded-${Date.now()}`,
timeStamp: new Date()
};
chatRef.current?.addMessage(forwardedMsg);
}
};Message Status
Configure Message Status
Track message delivery state with status icons and text:
import { ChatUIComponent, MessageStatusModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const user: UserModel = { id: "user1", user: "Albert" };
const agent: UserModel = { id: "user2", user: "Support" };
const deliveredStatus: MessageStatusModel = {
iconCss: 'e-icons e-chat-delivered',
text: 'Delivered',
tooltip: 'Message delivered at 10:30 AM'
};
const readStatus: MessageStatusModel = {
iconCss: 'e-icons e-chat-seen',
text: 'Seen',
tooltip: 'Seen by recipient at 10:32 AM'
};
return (
<ChatUIComponent user={user}>
<MessagesDirective>
<MessageDirective
text="Message 1"
author={user}
status={deliveredStatus}
/>
<MessageDirective
text="Message 2"
author={user}
status={readStatus}
/>
</MessagesDirective>
</ChatUIComponent>
);
}Message Toolbar
Customize Message Toolbar
Configure toolbar items for message actions:
import { ChatUIComponent, MessageToolbarSettingsModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const messageToolbarSettings: MessageToolbarSettingsModel = {
width: '100%',
items: [
{ type: 'Button', iconCss: 'e-icons e-chat-forward', tooltip: 'Forward' },
{ type: 'Button', iconCss: 'e-icons e-chat-copy', tooltip: 'Copy' },
{ type: 'Button', iconCss: 'e-icons e-chat-reply', tooltip: 'Reply' },
{ type: 'Button', iconCss: 'e-icons e-chat-pin', tooltip: 'Pin' },
{ type: 'Button', iconCss: 'e-icons e-chat-trash', tooltip: 'Delete' }
]
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
messageToolbarSettings={messageToolbarSettings}
/>
);
}Handle Toolbar Item Click
const messageToolbarSettings: MessageToolbarSettingsModel = {
items: [
{ type: 'Button', iconCss: 'e-icons e-chat-copy', tooltip: 'Copy' },
{ type: 'Button', iconCss: 'e-icons e-chat-trash', tooltip: 'Delete' }
],
itemClicked: (args) => {
if (args.item.iconCss === 'e-icons e-chat-copy') {
navigator.clipboard.writeText(args.message.text);
console.log('Copied to clipboard');
} else if (args.item.iconCss === 'e-icons e-chat-trash') {
console.log('Delete message:', args.message.id);
}
}
};Markdown Content
Enable Markdown Rendering
Support rich text formatting in messages:
import { ChatUIComponent, MessageModel, MessageSentEventArgs } from '@syncfusion/ej2-react-interactive-chat';
import { marked } from 'marked';
import DOMPurify from 'dompurify';
import React, { useState } from 'react';
function App() {
const [messages, setMessages] = useState<MessageModel[]>([
{
text: marked.parse('**Important:** This is bold text'),
author: { id: "user2", user: "Agent" }
}
]);
const user = { id: "user1", user: "Albert" };
const handleMessageSend = (args: MessageSentEventArgs) => {
args.cancel = true;
const parsedText = DOMPurify.sanitize(marked.parse(args.message.text));
const newMessage: MessageModel = {
text: parsedText,
author: user,
timeStamp: new Date()
};
setMessages([...messages, newMessage]);
};
return (
<ChatUIComponent
user={user}
messages={messages}
messageSend={handleMessageSend}
/>
);
}Supported Markdown
- Bold:
**text**or__text__ - Italic:
*text*or_text_ - Links:
[text](url) - Lists:
- itemor1. item - Code: `
code`
Best Practices
1. Always assign message IDs for programmatic access and updates 2. Use timestamps for message ordering and history 3. Implement message status for better UX and feedback 4. Sanitize markdown content to prevent XSS attacks 5. Handle message limits to prevent performance issues with large conversations
Templating System
Table of Contents
- Empty Chat Template
- Message Template
- Time Break Template
- Typing Users Template
- Suggestion Template
- Footer Template
Empty Chat Template
Display Welcome Message
Show custom content when chat has no messages:
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const emptyChatTemplate = () => {
return (
<div style={{
textAlign: 'center',
padding: '40px',
color: '#666'
}}>
<h3 style={{ fontSize: '24px', marginBottom: '10px' }}>
<span className="e-icons e-chat"></span>
</h3>
<h4>No Messages Yet</h4>
<p>Start a conversation to see your messages here.</p>
</div>
);
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
emptyChatTemplate={emptyChatTemplate}
/>
);
}Message Template
Custom Message Rendering
Control how each message is displayed:
import { ChatUIComponent, MessagesDirective, MessageDirective, MessageModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const messageTemplate = (context: { message: MessageModel; index: number }) => {
const isCurrentUser = context.message.author?.id === "user1";
return (
<div style={{
marginBottom: '12px',
textAlign: isCurrentUser ? 'right' : 'left'
}}>
<div style={{
display: 'inline-block',
backgroundColor: isCurrentUser ? '#0078d4' : '#f0f0f0',
color: isCurrentUser ? 'white' : 'black',
padding: '10px 16px',
borderRadius: '12px',
maxWidth: '70%',
wordWrap: 'break-word'
}}>
{context.message.text}
</div>
</div>
);
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
messageTemplate={messageTemplate}
>
<MessagesDirective>
<MessageDirective text="Hello!" author={{ id: "user1", user: "Albert" }} />
<MessageDirective text="Hi there!" author={{ id: "user2", user: "Support" }} />
</MessagesDirective>
</ChatUIComponent>
);
}Message Template with Rich Content
const messageTemplate = (context) => {
const message = context.message;
const user = message.author;
return (
<div className="e-card" style={{ marginBottom: '10px' }}>
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '8px' }}>
<strong>{user.user}</strong>
<span style={{ fontSize: '12px', marginLeft: '8px', color: '#999' }}>
{new Date(message.timeStamp).toLocaleTimeString()}
</span>
</div>
<div style={{ padding: '8px 0' }}>
{message.text}
</div>
{message.status && (
<div style={{ fontSize: '12px', color: '#999', marginTop: '4px' }}>
{message.status.text}
</div>
)}
</div>
);
};Time Break Template
Custom Date Separator
Customize how date separators appear:
import { ChatUIComponent, MessagesDirective, MessageDirective } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const timeBreakTemplate = (context) => {
const date = new Date(context.messageDate);
const formattedDate = date.toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
});
return (
<div style={{
textAlign: 'center',
margin: '16px 0',
color: '#999'
}}>
<span style={{
backgroundColor: '#f0f0f0',
padding: '4px 12px',
borderRadius: '12px',
fontSize: '12px'
}}>
{formattedDate}
</span>
</div>
);
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
showTimeBreak={true}
timeBreakTemplate={timeBreakTemplate}
>
<MessagesDirective>
<MessageDirective
text="Message from today"
author={{ id: "user1", user: "Albert" }}
timeStamp={new Date()}
/>
<MessageDirective
text="Message from yesterday"
author={{ id: "user2", user: "Support" }}
timeStamp={new Date(Date.now() - 86400000)}
/>
</MessagesDirective>
</ChatUIComponent>
);
}Typing Users Template
Custom Typing Indicator
Display who is typing with custom styling:
import { ChatUIComponent, UserModel } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const typingUsers: UserModel[] = [
{ id: "user2", user: "Michale" },
{ id: "user3", user: "Reena" }
];
const typingUsersTemplate = (context) => {
if (!context.users || context.users.length === 0) {
return null;
}
const userNames = context.users
.map((u, i) => {
const isLast = i === context.users.length - 1;
const isSecondToLast = i === context.users.length - 2;
if (isLast && i > 0) return `and ${u.user}`;
if (isSecondToLast && context.users.length === 2) return u.user;
return u.user;
})
.join(', ');
return (
<div style={{
display: 'flex',
alignItems: 'center',
padding: '8px 0',
color: '#666',
fontSize: '14px'
}}>
<span>{userNames} {context.users.length === 1 ? 'is' : 'are'} typing</span>
<span style={{ marginLeft: '4px' }}>
<span style={{ animation: 'pulse 1s infinite' }}>•</span>
<span style={{ animation: 'pulse 1s infinite 0.2s' }}>•</span>
<span style={{ animation: 'pulse 1s infinite 0.4s' }}>•</span>
</span>
</div>
);
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
typingUsers={typingUsers}
typingUsersTemplate={typingUsersTemplate}
/>
);
}Suggestion Template
Custom Suggestion Buttons
Style suggestion/quick-reply buttons:
import { ChatUIComponent } from '@syncfusion/ej2-react-interactive-chat';
import React from 'react';
function App() {
const suggestions = [
"I need help with billing",
"How do I reset my password?",
"Check order status"
];
const suggestionTemplate = (context) => {
return (
<button style={{
backgroundColor: '#0078d4',
color: 'white',
border: 'none',
padding: '8px 16px',
borderRadius: '20px',
margin: '4px',
cursor: 'pointer',
fontSize: '14px'
}}>
{context.suggestion}
</button>
);
};
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
suggestions={suggestions}
suggestionTemplate={suggestionTemplate}
/>
);
}Footer Template
Custom Input Area
Replace the default footer with custom content:
import { ChatUIComponent, MessageModel } from '@syncfusion/ej2-react-interactive-chat';
import React, { useRef, useState } from 'react';
function App() {
const chatRef = useRef<ChatUIComponent>(null);
const [inputValue, setInputValue] = useState('');
const footerTemplate = () => {
const handleSend = () => {
if (inputValue.trim()) {
const newMessage: MessageModel = {
author: { id: "user1", user: "Albert" },
text: inputValue,
timeStamp: new Date()
};
chatRef.current?.addMessage(newMessage);
setInputValue('');
}
};
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSend();
}
};
return (
<div style={{
display: 'flex',
padding: '12px',
gap: '8px',
borderTop: '1px solid #e0e0e0'
}}>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="Type a message..."
style={{
flex: 1,
padding: '8px 12px',
border: '1px solid #d0d0d0',
borderRadius: '4px',
fontSize: '14px'
}}
/>
<button
onClick={handleSend}
style={{
backgroundColor: '#0078d4',
color: 'white',
border: 'none',
padding: '8px 16px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '14px'
}}
>
<span className="e-icons e-send-1"></span>
</button>
</div>
);
};
return (
<ChatUIComponent
ref={chatRef}
user={{ id: "user1", user: "Albert" }}
footerTemplate={footerTemplate}
/>
);
}Complete Template Example
Full Customization
function App() {
const emptyChatTemplate = () => (
<div style={{ textAlign: 'center', padding: '40px' }}>
<h3>Start a Conversation</h3>
<p>Select from suggestions below or type your message</p>
</div>
);
const messageTemplate = (ctx) => (
<div style={{
display: 'flex',
justifyContent: ctx.message.author?.id === "user1" ? 'flex-end' : 'flex-start',
marginBottom: '12px'
}}>
<div style={{
backgroundColor: ctx.message.author?.id === "user1" ? '#0078d4' : '#f0f0f0',
color: ctx.message.author?.id === "user1" ? 'white' : 'black',
padding: '10px 14px',
borderRadius: '8px',
maxWidth: '70%'
}}>
{ctx.message.text}
</div>
</div>
);
const typingUsersTemplate = (ctx) => (
<div style={{ color: '#999', fontSize: '12px', padding: '8px 0' }}>
{ctx.users?.map(u => u.user).join(', ')} typing...
</div>
);
return (
<ChatUIComponent
user={{ id: "user1", user: "Albert" }}
emptyChatTemplate={emptyChatTemplate}
messageTemplate={messageTemplate}
typingUsersTemplate={typingUsersTemplate}
/>
);
}Best Practices
1. Keep templates simple for better performance 2. Use memoization for complex templates 3. Handle null/undefined gracefully 4. Match design system styling 5. Test responsiveness across devices 6. Optimize re-renders with proper React keys
Related skills
How it compares
Pick syncfusion-react-chat-ui over generic React chat tutorials when you need Syncfusion-specific props, events, and import paths for Essential Studio Chat UI.
FAQ
Which package does syncfusion-react-chat-ui import?
syncfusion-react-chat-ui imports ChatUIComponent, MessagesDirective, MessageDirective, and UserModel from @syncfusion/ej2-react-interactive-chat. The quick-start example wires current and other user models with headerText for a support chat layout.
How do you add messages dynamically in Syncfusion Chat UI?
syncfusion-react-chat-ui shows a useRef on ChatUIComponent and calls chatRef.current.addMessage() with author, text, and timeStamp fields. This pattern supports real-time message injection without remounting the component.
What reference guides ship with syncfusion-react-chat-ui?
syncfusion-react-chat-ui links 11 references/ files covering getting started, message management, user config, events, templating, attachments, headers, timestamps, typing indicators, mentions, and globalization RTL support.