
Syncfusion Blazor Chat Ui
- 240 installs
- 4 repo stars
- Updated July 28, 2026
- syncfusion/blazor-ui-components-skills
Use syncfusion-blazor-chat-ui for development tasks
About
syncfusion-blazor-chat-ui: A skill for development. This provides functionality for development workflows.
- syncfusion-blazor-chat-ui
Syncfusion Blazor Chat Ui by the numbers
- 240 all-time installs (skills.sh)
- +13 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,635 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/blazor-ui-components-skills --skill syncfusion-blazor-chat-uiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 240 |
|---|---|
| repo stars | ★ 4 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/blazor-ui-components-skills ↗ |
What it does
Use syncfusion-blazor-chat-ui for development tasks
Files
Syncfusion Blazor Chat UI
Build feature-rich conversational chat interfaces with the Syncfusion Blazor Chat UI component. This skill provides complete guidance for creating multi-user messaging applications, chat panels, and interactive conversation UIs in Blazor Server, WebAssembly, and Web App projects.
Component Overview
The SfChatUI component is a specialized UI control for building conversational chat applications. It manages the complete chat interface including:
- Message Management: Collections of chat messages with full message lifecycle
- User Management: Multi-user support with profiles, avatars, and status
- Presence Features: Typing indicators, online status, user identification
- Rich Rendering: Templates for messages, timestamps, typing indicators, suggestions
- File Support: File attachment upload and integration
- Event System: Lifecycle events and user interaction callbacks
- Customization: Complete styling and theming control
Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Installation and NuGet package setup
- Basic component structure and initialization
- User and message configuration
- Minimal working example with conversation
Messages & Users Management
📄 Read: references/messages-and-users.md
- ChatMessage configuration and properties
- UserModel setup and properties
- Message text content management
- Author identification and user assignment
- Building message collections and conversation history
User Profiles & Avatars
📄 Read: references/user-profiles-avatars.md
- Avatar URL configuration with images
- Avatar background color customization
- User status indicators (online, offline, busy, away)
- Custom CSS styling for users
- User identification and display names
Typing Indicators
📄 Read: references/typing-indicators.md
- TypingUsers property configuration
- Showing/hiding typing status indicators
- Managing multiple typing users
- Dynamic typing status updates
- Typing indicator template customization
Timestamps & Formatting
📄 Read: references/timestamps-and-formatting.md
- ShowTimestamp property to enable/disable timestamps
- Message timestamp configuration
- TimestampFormat customization
- TimeBreak separators (Today, Yesterday, specific dates)
- TimeBreak template styling
Templates & Customization
📄 Read: references/templates-and-customization.md
- EmptyChatTemplate for initial state display
- MessageTemplate for custom message rendering
- TimeBreakTemplate for date separators
- TypingUsersTemplate for typing display
- SuggestionTemplate for quick reply buttons
- CSS styling and theme integration
Attachments & Events
📄 Read: references/attachments-and-events.md
- File attachment configuration (Enable)
- SaveUrl and RemoveUrl endpoint setup
- AllowedFileTypes filtering
- MaxFileSize limits and constraints
- SaveFormat options (Blob vs Base64)
- Event handling: Created, MessageSend, UserTyping
- Attachment events: OnAttachmentUploadReady, UploadSuccess, UploadFailed
Programmatic API & Methods 🆕
📄 Read: references/programmatic-api.md
- ScrollToBottomAsync() for navigating to latest messages
- ScrollToMessageAsync() for jumping to specific messages
- UpdateMessageAsync() for editing existing messages
- FocusAsync() for input field control
- Complete examples with error handling
Advanced Features 🆕
📄 Read: references/advanced-features.md
- AutoScrollToBottom configuration
- EnableCompactMode for group chats
- Header and Footer customization
- Mention system (@mention) setup
- Quick reply suggestions
- LoadOnDemand for performance
- RTL (Right-to-Left) support
- Custom styling with CssClass
Quick Start
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 500px; width: 600px;">
<SfChatUI ID="chatUser" User="CurrentUser" Messages="Messages"></SfChatUI>
</div>
@code {
private UserModel CurrentUser = new UserModel
{
ID = "user1",
User = "Albert",
AvatarBgColor = "#4a90e2"
};
private UserModel OtherUser = new UserModel
{
ID = "user2",
User = "Michale Suyama",
AvatarBgColor = "#7cb342"
};
private List<ChatMessage> Messages = new()
{
new ChatMessage
{
Text = "Hi, how are you?",
Author = new UserModel { ID = "user1", User = "Albert" }
},
new ChatMessage
{
Text = "I'm doing great! How about you?",
Author = new UserModel { ID = "user2", User = "Michale Suyama" }
}
};
}Common Patterns
Pattern 1: Multi-User Conversation with Status
Display multiple users with online/offline status indicators:
<SfChatUI ID="chat" User="CurrentUser" Messages="Messages">
</SfChatUI>
@code {
private UserModel CurrentUser = new UserModel
{
ID = "user1",
User = "Albert",
StatusIconCss = "e-icons e-user-online"
};
private List<ChatMessage> Messages = new();
}Pattern 2: Chat with Typing Indicators
Show when users are typing:
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
TypingUsers="TypingUsers">
</SfChatUI>
@code {
private List<UserModel> TypingUsers = new();
private void UserStartTyping(UserModel user)
{
TypingUsers.Add(user);
StateHasChanged();
}
}Pattern 3: Custom Message Templates
Personalize message appearance:
<SfChatUI ID="chat" User="CurrentUser" Messages="Messages">
<MessageTemplate>
<div class="custom-message">
<strong>@context.Message.Author.User</strong>
<p>@((MarkupString)context.Message.Text)</p>
</div>
</MessageTemplate>
</SfChatUI>Pattern 4: Chat with File Attachments
Enable users to share files:
<SfChatUI ID="chat" User="CurrentUser" Messages="Messages">
<ChatUIAttachment
Enable
SaveUrl="@SaveUrl"
RemoveUrl="@RemoveUrl"
AllowedFileTypes=".pdf,.docx,.jpg,.png">
</ChatUIAttachment>
</SfChatUI>
@code {
private string SaveUrl = "api/upload/save";
private string RemoveUrl = "api/upload/remove";
}Pattern 5: Auto-Scroll with Programmatic Control
Automatically scroll to bottom and navigate to specific messages:
<SfChatUI
@ref="chatRef"
ID="chat"
User="CurrentUser"
Messages="Messages"
AutoScrollToBottom="true">
</SfChatUI>
<button @onclick="ScrollToBottom">Go to Latest</button>
<button @onclick="FocusInput">Focus Input</button>
@code {
private SfChatUI chatRef;
private async Task ScrollToBottom()
{
await chatRef.ScrollToBottomAsync();
}
private async Task FocusInput()
{
await chatRef.FocusAsync();
}
}Pattern 6: Message Editing with UpdateMessageAsync
Edit existing messages programmatically:
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages">
</SfChatUI>
@code {
private SfChatUI chatRef;
private async Task EditMessage(string messageId, string newText)
{
var message = Messages.FirstOrDefault(m => m.ID == messageId);
if (message != null)
{
message.Text = newText;
await chatRef.UpdateMessageAsync(message, messageId);
}
}
}Pattern 7: Mention System for User Tagging
Enable users to mention others in messages:
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
MentionChar="@"
MentionUsers="AllUsers"
ValueSelecting="@OnMentionSelected">
</SfChatUI>
@code {
private List<UserModel> AllUsers = new()
{
new UserModel { ID = "user1", User = "Albert" },
new UserModel { ID = "user2", User = "Michale" },
new UserModel { ID = "user3", User = "Reena" }
};
private void OnMentionSelected(MentionValueSelectingEventArgs<UserModel> args)
{
Console.WriteLine($"User mentioned: {args.ItemData.User}");
}
}Pattern 8: Compact Mode for Group Chats
Display all messages left-aligned for group chat style:
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
EnableCompactMode="true">
</SfChatUI>
@code {
// In compact mode, all messages appear on left side
// Useful for group chat or support ticket interfaces
}Key Props
| Property | Type | Purpose |
|---|---|---|
ID | string | Unique component identifier |
User | UserModel | Current logged-in user |
Messages | List\<ChatMessage\> | Conversation messages collection |
ShowTimestamp | bool | Display message timestamps (default: true) |
TimestampFormat | string | Date/time format (default: "dd/MM/yyyy hh:mm tt") |
ShowTimeBreak | bool | Display date separators between messages |
TypingUsers | List\<UserModel\> | Users currently typing |
AutoScrollToBottom | bool | Auto-scroll to bottom on new messages (default: false) |
EnableCompactMode | bool | Align all messages to left side (default: false) |
Height | string | Component height (default: "100%") |
Width | string | Component width (default: "100%") |
HeaderText | string | Header text display (default: "Chat") |
ShowHeader | bool | Show/hide header (default: true) |
ShowFooter | bool | Show/hide footer (default: true) |
Placeholder | string | Input placeholder text (default: "Type your message…") |
Suggestions | List\<string\> | Quick reply suggestions |
MentionChar | char | Mention trigger character (default: '@') |
MentionUsers | List\<UserModel\> | Users available for mention |
LoadOnDemand | bool | Load messages on demand (default: false) |
CssClass | string | Custom CSS styling |
EnableRtl | bool | Right-to-left direction support (default: false) |
EmptyChatTemplate | RenderFragment | Custom empty state content |
MessageTemplate | RenderFragment\<MessageTemplateContext\> | Custom message rendering |
TimeBreakTemplate | RenderFragment\<TimeBreakTemplateContext\> | Custom date separator |
TypingUsersTemplate | RenderFragment\<TypingUsersTemplateContext\> | Custom typing indicator |
SuggestionTemplate | RenderFragment\<SuggestionTemplateContext\> | Custom suggestion display |
FooterTemplate | RenderFragment | Custom footer area |
PreviewTemplate | RenderFragment\<PreviewTemplateContext\> | Custom attachment preview |
Programmatic Methods
The Chat UI component provides async methods for programmatic control:
ScrollToBottomAsync()
Scrolls the chat to the bottom, useful for showing latest messages:
@ref="chatRef"
private SfChatUI chatRef;
private async Task ShowLatestMessages()
{
await chatRef.ScrollToBottomAsync();
}ScrollToMessageAsync(string messageId)
Navigates to a specific message by ID:
private async Task NavigateToMessage(string targetMessageId)
{
await chatRef.ScrollToMessageAsync(targetMessageId);
}UpdateMessageAsync(ChatMessage message, string msgId)
Updates an existing message content:
private async Task EditMessage(string messageId, string newText)
{
var message = Messages.FirstOrDefault(m => m.ID == messageId);
if (message != null)
{
message.Text = newText;
await chatRef.UpdateMessageAsync(message, messageId);
}
}FocusAsync()
Sets focus on the chat input field:
private async Task FocusChatInput()
{
await chatRef.FocusAsync();
}Common Use Cases
1. Customer Support Chat - Live support widget with agent presence 2. Team Messaging - Internal communication platform 3. Bot Integration - AI chatbot with user interface 4. Community Chat - Multi-user conversation rooms 5. Help Desk - Ticketing and messaging interface 6. Social Features - In-app messaging and notifications 7. Real-time Collaboration - Live chat during shared activities
Advanced Features & Properties
Table of Contents
- Auto-Scroll Configuration
- Compact Mode
- Header & Footer Customization
- Component Sizing
- Mention System
- Quick Reply Suggestions
- Load on Demand
- Right-to-Left (RTL) Support
- Custom Styling
---
Auto-Scroll Configuration
AutoScrollToBottom Property
Type: bool Default: false
Automatically scrolls the chat to the bottom when new messages are received.
Basic Usage
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
AutoScrollToBottom="true">
</SfChatUI>Pattern: Conditional Auto-Scroll
<label>
<input type="checkbox" @bind="enableAutoScroll" />
Auto-scroll enabled
</label>
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
AutoScrollToBottom="@enableAutoScroll">
</SfChatUI>
@code {
private bool enableAutoScroll = true;
}Use Cases
- Real-time chat applications
- Live support conversations
- Streaming message feeds
- Bot conversations with auto-responses
---
Compact Mode
EnableCompactMode Property
Type: bool Default: false
When enabled, displays all messages aligned to the left side, regardless of author.
Basic Usage
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
EnableCompactMode="true">
</SfChatUI>Comparison
Normal Mode (Default):
- Current user messages: right-aligned
- Other user messages: left-aligned
Compact Mode:
- All messages: left-aligned
- User identification via avatar and name
Pattern: Switchable View Mode
<div class="view-toggle">
<button @onclick="@(() => compactMode = false)">Standard View</button>
<button @onclick="@(() => compactMode = true)">Compact View</button>
</div>
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
EnableCompactMode="@compactMode">
</SfChatUI>
@code {
private bool compactMode = false;
}Use Cases
- Group chat interfaces
- Support ticket history
- Forum-style conversations
- Multi-user team chats
- Message threads and replies
---
Header & Footer Customization
Header Properties
| Property | Type | Default | Purpose |
|---|---|---|---|
ShowHeader | bool | true | Show/hide header bar |
HeaderText | string | "Chat" | Header title text |
HeaderIconCss | string | Empty | CSS class for header icon |
Footer Properties
| Property | Type | Default | Purpose |
|---|---|---|---|
ShowFooter | bool | true | Show/hide footer input area |
FooterTemplate | RenderFragment | null | Custom footer content |
Pattern 1: Custom Header
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
ShowHeader="true"
HeaderText="Support Chat"
HeaderIconCss="e-icons e-comment-show">
</SfChatUI>Pattern 2: Hidden Header
<!-- Embedded chat widget without header -->
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
ShowHeader="false">
</SfChatUI>Pattern 3: Custom Footer Template
<SfChatUI ID="chat" User="CurrentUser" Messages="Messages">
<FooterTemplate>
<div class="custom-footer">
<button @onclick="AttachFile">📎 Attach</button>
<input type="text" placeholder="Type message..." />
<button @onclick="SendMessage">Send</button>
</div>
</FooterTemplate>
</SfChatUI>
@code {
private void AttachFile()
{
// Custom file attachment logic
}
private void SendMessage()
{
// Custom send logic
}
}Pattern 4: Dynamic Header Based on Context
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
HeaderText="@GetHeaderText()"
HeaderIconCss="@GetHeaderIcon()">
</SfChatUI>
@code {
private string conversationType = "support"; // "support", "team", "personal"
private string GetHeaderText()
{
return conversationType switch
{
"support" => "Customer Support",
"team" => "Team Discussion",
"personal" => "Direct Message",
_ => "Chat"
};
}
private string GetHeaderIcon()
{
return conversationType switch
{
"support" => "e-icons e-help",
"team" => "e-icons e-people",
"personal" => "e-icons e-comment",
_ => ""
};
}
}---
Component Sizing
Height and Width Properties
| Property | Type | Default | Purpose |
|---|---|---|---|
Height | string | "100%" | Component height (CSS value) |
Width | string | "100%" | Component width (CSS value) |
Pattern 1: Fixed Dimensions
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
Height="500px"
Width="400px">
</SfChatUI>Pattern 2: Responsive Sizing
<!-- Full viewport height -->
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
Height="100vh"
Width="100%">
</SfChatUI>Pattern 3: Container-Based Sizing
<div style="display: flex; height: 600px; width: 800px;">
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
Height="100%"
Width="100%">
</SfChatUI>
</div>Pattern 4: Mobile-Responsive
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
Height="@chatHeight"
Width="@chatWidth">
</SfChatUI>
@code {
private string chatHeight => IsMobile ? "100vh" : "600px";
private string chatWidth => IsMobile ? "100%" : "400px";
private bool IsMobile => /* detect mobile device */;
}---
Mention System
Mention Properties
| Property | Type | Default | Purpose |
|---|---|---|---|
MentionChar | char | '@' | Character triggering mention popup |
MentionUsers | List\<UserModel\> | Empty | Users available for mention |
ValueSelecting | EventCallback | null | Event when mention selected |
Basic Mention Setup
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
MentionChar="@"
MentionUsers="@teamMembers"
ValueSelecting="@OnMentionSelected">
</SfChatUI>
@code {
private List<UserModel> teamMembers = new()
{
new UserModel { ID = "user1", User = "Albert" },
new UserModel { ID = "user2", User = "Michale Suyama" },
new UserModel { ID = "user3", User = "Reena" },
new UserModel { ID = "user4", User = "Laura Callahan" }
};
private void OnMentionSelected(MentionValueSelectingEventArgs<UserModel> args)
{
Console.WriteLine($"Mentioned user: {args.ItemData.User}");
// Send notification to mentioned user
}
}Pattern 1: Custom Mention Character
<!-- Use # for hashtags instead of @ for mentions -->
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
MentionChar="#"
MentionUsers="@channels">
</SfChatUI>
@code {
private List<UserModel> channels = new()
{
new UserModel { ID = "general", User = "general" },
new UserModel { ID = "support", User = "support" },
new UserModel { ID = "dev", User = "dev-team" }
};
}Pattern 2: Dynamic Mention List
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
MentionChar="@"
MentionUsers="@GetAvailableUsers()"
ValueSelecting="@OnMentionSelected">
</SfChatUI>
@code {
private List<UserModel> allUsers = new();
private List<UserModel> onlineUsers = new();
private List<UserModel> GetAvailableUsers()
{
// Only show online users in mention list
return onlineUsers.Where(u => u.ID != CurrentUser.ID).ToList();
}
private async Task OnMentionSelected(MentionValueSelectingEventArgs<UserModel> args)
{
// Send real-time notification
await NotificationService.NotifyUser(args.ItemData.ID, "You were mentioned");
}
}Pattern 3: Mention with Notification
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
MentionChar="@"
MentionUsers="@teamMembers"
ValueSelecting="@OnMentionSelected"
MessageSend="@OnMessageSend">
</SfChatUI>
@code {
private List<string> mentionedUsers = new();
private void OnMentionSelected(MentionValueSelectingEventArgs<UserModel> args)
{
mentionedUsers.Add(args.ItemData.ID);
}
private async Task OnMessageSend(ChatMessageSendEventArgs args)
{
// Send message
Messages.Add(args.Message);
// Notify all mentioned users
foreach (var userId in mentionedUsers)
{
await SendNotification(userId, args.Message.Text);
}
mentionedUsers.Clear();
StateHasChanged();
}
}---
Quick Reply Suggestions
Suggestion Properties
| Property | Type | Default | Purpose |
|---|---|---|---|
Suggestions | List\<string\> | Empty | Quick reply suggestion texts |
SuggestionTemplate | RenderFragment | null | Custom suggestion rendering |
Basic Suggestions
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
Suggestions="@quickReplies">
</SfChatUI>
@code {
private List<string> quickReplies = new()
{
"Yes, please",
"No, thank you",
"Tell me more",
"I need help"
};
}Pattern 1: Dynamic Suggestions
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
Suggestions="@GetContextualSuggestions()">
</SfChatUI>
@code {
private string lastBotQuestion = "";
private List<string> GetContextualSuggestions()
{
return lastBotQuestion.ToLower() switch
{
var q when q.Contains("help") => new() { "Technical Support", "Billing", "Account" },
var q when q.Contains("payment") => new() { "Credit Card", "PayPal", "Bank Transfer" },
var q when q.Contains("shipping") => new() { "Standard", "Express", "Overnight" },
_ => new() { "Yes", "No", "Maybe" }
};
}
}Pattern 2: Custom Suggestion Template
<SfChatUI ID="chat" User="CurrentUser" Messages="Messages" Suggestions="@suggestions">
<SuggestionTemplate>
<div class="custom-suggestion">
<span class="suggestion-icon">💡</span>
<span class="suggestion-text">@context</span>
</div>
</SuggestionTemplate>
</SfChatUI>
@code {
private List<string> suggestions = new() { "Option 1", "Option 2", "Option 3" };
}Pattern 3: Suggestion with Actions
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
Suggestions="@suggestionTexts"
MessageSend="@OnMessageSend">
</SfChatUI>
@code {
private List<string> suggestionTexts = new();
private void ShowSuggestions(List<string> options)
{
suggestionTexts = options;
StateHasChanged();
}
private async Task OnMessageSend(ChatMessageSendEventArgs args)
{
// User selected a suggestion
if (suggestionTexts.Contains(args.Message.Text))
{
// Clear suggestions after selection
suggestionTexts.Clear();
}
Messages.Add(args.Message);
// Show new suggestions based on response
await ProcessUserResponse(args.Message.Text);
}
}---
Load on Demand
LoadOnDemand Property
Type: bool Default: false
Enables on-demand loading of chat messages for better performance with large message histories.
Basic Usage
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
LoadOnDemand="true">
</SfChatUI>Pattern: Lazy Loading Messages
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
LoadOnDemand="true">
</SfChatUI>
<button @onclick="LoadMoreMessages">Load Older Messages</button>
@code {
private List<ChatMessage> Messages = new();
private int currentPage = 0;
private const int pageSize = 50;
protected override async Task OnInitializedAsync()
{
await LoadMoreMessages();
}
private async Task LoadMoreMessages()
{
var olderMessages = await FetchMessagesFromDatabase(currentPage, pageSize);
Messages.InsertRange(0, olderMessages);
currentPage++;
StateHasChanged();
}
private async Task<List<ChatMessage>> FetchMessagesFromDatabase(int page, int size)
{
// Database query with pagination
await Task.Delay(500); // Simulate database call
return new List<ChatMessage>(); // Return fetched messages
}
}---
Right-to-Left (RTL) Support
EnableRtl Property
Type: bool Default: false
Enables right-to-left text direction for languages like Arabic, Hebrew, Persian, etc.
Basic Usage
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
EnableRtl="true">
</SfChatUI>Pattern: Language-Based RTL
<select @bind="selectedLanguage" @bind:after="UpdateRtl">
<option value="en">English</option>
<option value="ar">Arabic</option>
<option value="he">Hebrew</option>
</select>
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
EnableRtl="@isRtl">
</SfChatUI>
@code {
private string selectedLanguage = "en";
private bool isRtl = false;
private void UpdateRtl()
{
isRtl = selectedLanguage is "ar" or "he" or "fa" or "ur";
}
}---
Custom Styling
CssClass Property
Type: string Default: String.Empty
Applies custom CSS classes to the Chat UI component for styling customization.
Basic Usage
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
CssClass="custom-chat-theme">
</SfChatUI>
<style>
.custom-chat-theme {
--chat-primary-color: #4a90e2;
--chat-bg-color: #f5f5f5;
}
</style>Pattern 1: Theme Switching
<select @bind="selectedTheme">
<option value="light-theme">Light</option>
<option value="dark-theme">Dark</option>
<option value="blue-theme">Blue</option>
</select>
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
CssClass="@selectedTheme">
</SfChatUI>
@code {
private string selectedTheme = "light-theme";
}Pattern 2: Branded Chat
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
CssClass="company-branded-chat">
</SfChatUI>
<style>
.company-branded-chat {
font-family: 'Company Font', sans-serif;
border: 2px solid #company-color;
border-radius: 12px;
}
.company-branded-chat .e-chat-message {
background-color: #brand-light;
}
</style>---
Best Practices
1. Performance - Use LoadOnDemand for chats with 100+ messages 2. Accessibility - Enable RTL for appropriate languages 3. UX - Use AutoScrollToBottom for real-time conversations 4. Mobile - Use responsive Height and Width values 5. Mentions - Provide clear feedback when users are mentioned 6. Suggestions - Keep suggestion lists short (3-5 options) 7. Compact Mode - Use for multi-user or group conversations 8. Styling - Use CssClass for consistent branding
Attachments & Events
Table of Contents
- File Attachments Configuration
- Save and Remove URLs
- File Type Restrictions
- File Size Limits
- Event Handling
- Attachment Events
- OnAttachmentUploadReady
- AttachmentUploadSuccess
- AttachmentUploadFailed
- AttachmentClick
- AttachmentRemoved
- Mention Events
File Attachments Configuration
The ChatUIAttachment tag enables file attachment support in the Chat UI component. Set the Enable property to true to activate file uploads.
Enable File Attachments
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 600px;">
<SfChatUI ID="chatUser" User="CurrentUserModel" Messages="ChatUserMessages">
<ChatUIAttachment Enable="true">
</ChatUIAttachment>
</SfChatUI>
</div>
@code {
private UserModel CurrentUserModel = new UserModel()
{
ID = "User1",
User = "Albert"
};
private List<ChatMessage> ChatUserMessages = new();
}Disable File Attachments
<SfChatUI ID="chat" User="CurrentUser" Messages="Messages">
<ChatUIAttachment Enable="false">
</ChatUIAttachment>
</SfChatUI>Save and Remove URLs
Set the SaveUrl and RemoveUrl properties to specify server endpoints for handling file uploads and removals.
Basic Configuration
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 600px;">
<SfChatUI ID="chatUser" User="CurrentUserModel" Messages="ChatUserMessages">
<ChatUIAttachment
Enable="true"
SaveUrl="@SaveUrl"
RemoveUrl="@RemoveUrl">
</ChatUIAttachment>
</SfChatUI>
</div>
@code {
private string SaveUrl = "https://api.example.com/upload/save";
private string RemoveUrl = "https://api.example.com/upload/remove";
private UserModel CurrentUserModel = new UserModel()
{
ID = "User1",
User = "Albert"
};
private List<ChatMessage> ChatUserMessages = new();
}Controller Implementation (Server-Side)
// API Controller
[ApiController]
[Route("api/[controller]")]
public class UploadController : ControllerBase
{
[HttpPost("save")]
public IActionResult Save(IFormFile file)
{
try
{
if (file != null && file.Length > 0)
{
// Save file to server
var fileName = Guid.NewGuid().ToString() + Path.GetExtension(file.FileName);
var filePath = Path.Combine("uploads", fileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
file.CopyTo(stream);
}
return Ok(new { name = fileName, size = file.Length });
}
return BadRequest("No file uploaded");
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}
[HttpPost("remove")]
public IActionResult Remove([FromBody] string fileName)
{
try
{
var filePath = Path.Combine("uploads", fileName);
if (System.IO.File.Exists(filePath))
{
System.IO.File.Delete(filePath);
return Ok();
}
return NotFound("File not found");
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}
}File Type Restrictions
The AllowedFileTypes property controls which file types users can upload using file extensions or MIME types.
Restricting File Types
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 600px;">
<SfChatUI ID="chatUser" User="CurrentUserModel" Messages="ChatUserMessages">
<!-- Allow only PDF files -->
<ChatUIAttachment
Enable="true"
AllowedFileTypes=".pdf"
SaveUrl="@SaveUrl"
RemoveUrl="@RemoveUrl">
</ChatUIAttachment>
</SfChatUI>
</div>
@code {
private string SaveUrl = "https://api.example.com/upload/save";
private string RemoveUrl = "https://api.example.com/upload/remove";
private UserModel CurrentUserModel = new UserModel()
{
ID = "User1",
User = "Albert"
};
private List<ChatMessage> ChatUserMessages = new();
}Multiple File Types
<!-- Allow images and documents -->
<ChatUIAttachment
Enable="true"
AllowedFileTypes=".jpg,.jpeg,.png,.gif,.pdf,.docx,.xlsx"
SaveUrl="@SaveUrl"
RemoveUrl="@RemoveUrl">
</ChatUIAttachment>File Type Categories
<!-- Documents only -->
<ChatUIAttachment
AllowedFileTypes=".pdf,.docx,.doc,.xlsx,.xls,.pptx,.txt"
SaveUrl="@SaveUrl"
RemoveUrl="@RemoveUrl">
</ChatUIAttachment>
<!-- Images only -->
<ChatUIAttachment
AllowedFileTypes=".jpg,.jpeg,.png,.gif,.bmp,.svg,.webp"
SaveUrl="@SaveUrl"
RemoveUrl="@RemoveUrl">
</ChatUIAttachment>
<!-- Media files -->
<ChatUIAttachment
AllowedFileTypes=".mp3,.mp4,.avi,.mov,.wav,.flv"
SaveUrl="@SaveUrl"
RemoveUrl="@RemoveUrl">
</ChatUIAttachment>File Size Limits
The MaxFileSize property defines the maximum file size in bytes. Default is 30000000 bytes (≈30 MB).
Setting File Size Limit
<ChatUIAttachment
Enable="true"
MaxFileSize="4000000" <!-- 4 MB limit -->
SaveUrl="@SaveUrl"
RemoveUrl="@RemoveUrl">
</ChatUIAttachment>Common Size Configurations
<!-- 1 MB limit -->
<ChatUIAttachment MaxFileSize="1000000" Enable SaveUrl="@SaveUrl" RemoveUrl="@RemoveUrl"></ChatUIAttachment>
<!-- 5 MB limit -->
<ChatUIAttachment MaxFileSize="5000000" Enable SaveUrl="@SaveUrl" RemoveUrl="@RemoveUrl"></ChatUIAttachment>
<!-- 10 MB limit -->
<ChatUIAttachment MaxFileSize="10000000" Enable SaveUrl="@SaveUrl" RemoveUrl="@RemoveUrl"></ChatUIAttachment>
<!-- 100 MB limit -->
<ChatUIAttachment MaxFileSize="100000000" Enable SaveUrl="@SaveUrl" RemoveUrl="@RemoveUrl"></ChatUIAttachment>Event Handling
Created Event
The Created event triggers when the component finishes rendering.
<SfChatUI ID="chat" User="CurrentUser" Messages="Messages" Created="@OnChatCreated"></SfChatUI>
@code {
private void OnChatCreated()
{
// Component initialization complete
StateHasChanged();
}
}Message Send Event
The MessageSend event fires when a message is being sent.
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
MessageSend="@OnMessageSend">
</SfChatUI>
@code {
private void OnMessageSend(ChatMessageSendEventArgs args)
{
// Handle message send
// args.Message contains the message text
}
}User Typing Event
The UserTyping event triggers when user types a message.
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
UserTyping="@OnUserTyping">
</SfChatUI>
@code {
private void OnUserTyping(ChatUserTypingEventArgs args)
{
// Handle user typing
// Send typing notification to other users
}
}Attachment Events
OnAttachmentUploadReady
The OnAttachmentUploadReady event fires before file upload begins.
<SfChatUI ID="chat" User="CurrentUser" Messages="Messages" OnAttachmentUploadReady="@OnUploadReady">
<ChatUIAttachment Enable SaveUrl="@SaveUrl" RemoveUrl="@RemoveUrl"></ChatUIAttachment>
</SfChatUI>
@code {
private string SaveUrl = "api/upload/save";
private string RemoveUrl = "api/upload/remove";
private void OnUploadReady(AttachmentUploadReadyEventArgs args)
{
// Validate file before upload
// Can cancel upload if needed: args.Cancel = true;
}
}AttachmentUploadSuccess
The AttachmentUploadSuccess event fires when file successfully uploads.
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
AttachmentUploadSuccess="@OnUploadSuccess">
<ChatUIAttachment Enable SaveUrl="@SaveUrl" RemoveUrl="@RemoveUrl"></ChatUIAttachment>
</SfChatUI>
@code {
private string SaveUrl = "api/upload/save";
private string RemoveUrl = "api/upload/remove";
private void OnUploadSuccess(SuccessEventArgs args)
{
// File uploaded successfully
// args contains file information
}
}AttachmentUploadFailed
The AttachmentUploadFailed event fires when file upload fails.
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
AttachmentUploadFailed="@OnUploadFailed">
<ChatUIAttachment Enable SaveUrl="@SaveUrl" RemoveUrl="@RemoveUrl"></ChatUIAttachment>
</SfChatUI>
@code {
private string SaveUrl = "api/upload/save";
private string RemoveUrl = "api/upload/remove";
private void OnUploadFailed(FailureEventArgs args)
{
// Handle upload failure
// Show error message to user
}
}AttachmentClick
The AttachmentClick event fires when an attachment item is clicked, either before sending or after the attachment is sent.
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
AttachmentClick="@OnAttachmentClick">
<ChatUIAttachment Enable SaveUrl="@SaveUrl" RemoveUrl="@RemoveUrl"></ChatUIAttachment>
</SfChatUI>
@code {
private string SaveUrl = "api/upload/save";
private string RemoveUrl = "api/upload/remove";
private void OnAttachmentClick(ChatAttachmentClickEventArgs args)
{
// Handle attachment click
// Access file info: args.SelectedFile
// Can cancel preview: args.Cancel = true;
Console.WriteLine($"Attachment clicked: {args.SelectedFile?.Name}");
}
}Use Case: Custom Attachment Preview
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
AttachmentClick="@OnAttachmentClick">
<ChatUIAttachment Enable SaveUrl="@SaveUrl" RemoveUrl="@RemoveUrl"></ChatUIAttachment>
</SfChatUI>
@code {
private string SaveUrl = "api/upload/save";
private string RemoveUrl = "api/upload/remove";
private void OnAttachmentClick(ChatAttachmentClickEventArgs args)
{
// Cancel default preview
args.Cancel = true;
// Implement custom preview logic
if (args.SelectedFile != null)
{
var fileExtension = Path.GetExtension(args.SelectedFile.Name);
if (fileExtension == ".pdf")
{
// Open PDF in custom viewer
OpenPdfViewer(args.SelectedFile);
}
else if (fileExtension == ".jpg" || fileExtension == ".png")
{
// Open image in custom lightbox
OpenImageLightbox(args.SelectedFile);
}
else
{
// Download the file
DownloadFile(args.SelectedFile);
}
}
}
private void OpenPdfViewer(FileInfo file) { /* Custom PDF viewer */ }
private void OpenImageLightbox(FileInfo file) { /* Custom image lightbox */ }
private void DownloadFile(FileInfo file) { /* Download logic */ }
}AttachmentRemoved
The AttachmentRemoved event fires when an attachment is being removed. Can be cancelled by setting args.Cancel = true.
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
AttachmentRemoved="@OnAttachmentRemoved">
<ChatUIAttachment Enable SaveUrl="@SaveUrl" RemoveUrl="@RemoveUrl"></ChatUIAttachment>
</SfChatUI>
@code {
private string SaveUrl = "api/upload/save";
private string RemoveUrl = "api/upload/remove";
private void OnAttachmentRemoved(RemovingEventArgs args)
{
// Handle attachment removal
// Can cancel removal: args.Cancel = true;
Console.WriteLine("Attachment removed");
}
}Use Case: Confirm Before Removing
@using Syncfusion.Blazor.InteractiveChat
@using Syncfusion.Blazor.Popups
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
AttachmentRemoved="@OnAttachmentRemoved">
<ChatUIAttachment Enable SaveUrl="@SaveUrl" RemoveUrl="@RemoveUrl"></ChatUIAttachment>
</SfChatUI>
<SfDialog @ref="confirmDialog" Width="300px" IsModal="true" Visible="false">
<DialogTemplates>
<Header>Confirm Removal</Header>
<Content>Are you sure you want to remove this attachment?</Content>
</DialogTemplates>
<DialogButtons>
<DialogButton Content="Yes" OnClick="@ConfirmRemoval" />
<DialogButton Content="No" OnClick="@CancelRemoval" />
</DialogButtons>
</SfDialog>
@code {
private string SaveUrl = "api/upload/save";
private string RemoveUrl = "api/upload/remove";
private UserModel CurrentUser = new() { ID = "user1", User = "Albert" };
private List<ChatMessage> Messages = new();
private SfDialog confirmDialog;
private RemovingEventArgs pendingRemovalArgs;
private async Task OnAttachmentRemoved(RemovingEventArgs args)
{
// Cancel the removal temporarily
args.Cancel = true;
pendingRemovalArgs = args;
// Show confirmation dialog
await confirmDialog.ShowAsync();
}
private async Task ConfirmRemoval()
{
// User confirmed, allow removal
if (pendingRemovalArgs != null)
{
pendingRemovalArgs.Cancel = false;
// Trigger cleanup logic
await CleanupAttachment();
}
await confirmDialog.HideAsync();
}
private async Task CancelRemoval()
{
// Keep the removal cancelled
await confirmDialog.HideAsync();
}
private async Task CleanupAttachment()
{
// Perform any additional cleanup
await Task.CompletedTask;
}
}Use Case: Track Attachment Lifecycle
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
AttachmentClick="@OnAttachmentClick"
OnAttachmentUploadReady="@OnUploadReady"
AttachmentUploadSuccess="@OnUploadSuccess"
AttachmentRemoved="@OnAttachmentRemoved">
<ChatUIAttachment Enable SaveUrl="@SaveUrl" RemoveUrl="@RemoveUrl"></ChatUIAttachment>
</SfChatUI>
@code {
private string SaveUrl = "api/upload/save";
private string RemoveUrl = "api/upload/remove";
private Dictionary<string, DateTime> attachmentLog = new();
private void OnUploadReady(AttachmentUploadReadyEventArgs args)
{
foreach (var file in args.FilesData)
{
attachmentLog[file.Name] = DateTime.Now;
Console.WriteLine($"Upload started: {file.Name}");
}
}
private void OnUploadSuccess(SuccessEventArgs args)
{
Console.WriteLine("Upload completed successfully");
}
private void OnAttachmentClick(ChatAttachmentClickEventArgs args)
{
if (args.SelectedFile != null && attachmentLog.ContainsKey(args.SelectedFile.Name))
{
Console.WriteLine($"Attachment accessed: {args.SelectedFile.Name} " +
$"(uploaded at {attachmentLog[args.SelectedFile.Name]})");
}
}
private void OnAttachmentRemoved(RemovingEventArgs args)
{
Console.WriteLine("Attachment removed from chat");
// Could log removal for audit purposes
}
}Complete Example with All Events
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 500px; width: 600px;">
<SfChatUI
ID="chatUser"
User="CurrentUser"
Messages="Messages"
Created="@OnChatCreated"
MessageSend="@OnMessageSend"
UserTyping="@OnUserTyping"
OnAttachmentUploadReady="@OnUploadReady"
AttachmentUploadSuccess="@OnUploadSuccess"
AttachmentUploadFailed="@OnUploadFailed">
<ChatUIAttachment
Enable="true"
SaveUrl="@SaveUrl"
RemoveUrl="@RemoveUrl"
AllowedFileTypes=".pdf,.docx,.jpg,.png"
MaxFileSize="5000000">
</ChatUIAttachment>
</SfChatUI>
</div>
@code {
private string SaveUrl = "api/upload/save";
private string RemoveUrl = "api/upload/remove";
private UserModel CurrentUser = new UserModel
{
ID = "user1",
User = "Albert"
};
private List<ChatMessage> Messages = new();
private void OnChatCreated()
{
Console.WriteLine("Chat UI created");
}
private void OnMessageSend(ChatMessageSendEventArgs args)
{
Console.WriteLine($"Message sent: {args.Message}");
// Process message
}
private void OnUserTyping(ChatUserTypingEventArgs args)
{
Console.WriteLine("User is typing");
// Send typing notification
}
private void OnUploadReady(AttachmentUploadReadyEventArgs args)
{
Console.WriteLine("Upload starting");
}
private void OnUploadSuccess(SuccessEventArgs args)
{
Console.WriteLine("File uploaded successfully");
}
private void OnUploadFailed(FailureEventArgs args)
{
Console.WriteLine("File upload failed");
}
}Mention Events
ValueSelecting
The ValueSelecting event occurs when a user selects a mention from the suggestion popup in the chat UI.
@using Syncfusion.Blazor.InteractiveChat
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
MentionChar="@"
MentionUsers="@AvailableUsers"
ValueSelecting="@OnMentionSelecting">
</SfChatUI>
@code {
private UserModel CurrentUser = new() { ID = "user1", User = "Albert" };
private List<ChatMessage> Messages = new();
private List<UserModel> AvailableUsers = new()
{
new UserModel { ID = "user2", User = "Michale Suyama" },
new UserModel { ID = "user3", User = "Reena" },
new UserModel { ID = "user4", User = "Janet" }
};
private void OnMentionSelecting(MentionValueSelectingEventArgs<UserModel> args)
{
// Handle mention selection
Console.WriteLine($"User mentioned: {args.ItemData.User}");
}
}Use Case: Track Mentions for Notifications
@using Syncfusion.Blazor.InteractiveChat
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
MentionChar="@"
MentionUsers="@AvailableUsers"
ValueSelecting="@OnMentionSelecting"
MessageSend="@OnMessageSend">
</SfChatUI>
@code {
private UserModel CurrentUser = new() { ID = "user1", User = "Albert" };
private List<ChatMessage> Messages = new();
private List<UserModel> mentionedUsers = new();
private List<UserModel> AvailableUsers = new()
{
new UserModel { ID = "user2", User = "Michale Suyama" },
new UserModel { ID = "user3", User = "Reena" },
new UserModel { ID = "user4", User = "Janet" }
};
private void OnMentionSelecting(MentionValueSelectingEventArgs<UserModel> args)
{
// Track mentioned users
if (!mentionedUsers.Any(u => u.ID == args.ItemData.ID))
{
mentionedUsers.Add(args.ItemData);
}
Console.WriteLine($"Mentioned: @{args.ItemData.User}");
}
private async Task OnMessageSend(ChatMessageSendEventArgs args)
{
// Send notifications to mentioned users
foreach (var user in mentionedUsers)
{
await SendNotification(user, args.Message.Text);
}
// Clear mentions for next message
mentionedUsers.Clear();
}
private async Task SendNotification(UserModel user, string message)
{
// Send notification to mentioned user
Console.WriteLine($"Notifying {user.User}: {message}");
await Task.CompletedTask;
}
}Use Case: Restrict Mentions Based on User Role
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
MentionChar="@"
MentionUsers="@GetMentionableUsers()"
ValueSelecting="@OnMentionSelecting">
</SfChatUI>
@code {
private UserModel CurrentUser = new()
{
ID = "user1",
User = "Albert",
// Custom property (extended UserModel)
};
private List<ChatMessage> Messages = new();
private List<UserModel> AllUsers = new()
{
new UserModel { ID = "user2", User = "Michale Suyama" },
new UserModel { ID = "user3", User = "Reena" },
new UserModel { ID = "admin1", User = "Admin User" }
};
private List<UserModel> GetMentionableUsers()
{
// Filter users based on current user's permissions
// For example, regular users can't mention admins
return AllUsers.Where(u => !u.User.Contains("Admin")).ToList();
}
private void OnMentionSelecting(MentionValueSelectingEventArgs<UserModel> args)
{
// Validate the mention
if (args.ItemData.User.Contains("Admin"))
{
// Could cancel if needed
Console.WriteLine("Admin users cannot be mentioned");
// args.Cancel = true; // If this property exists
}
else
{
Console.WriteLine($"Valid mention: @{args.ItemData.User}");
}
}
}Use Case: Custom Mention Display Format
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
MentionChar="@"
MentionUsers="@TeamMembers"
ValueSelecting="@OnMentionSelecting">
</SfChatUI>
@code {
private UserModel CurrentUser = new() { ID = "user1", User = "Albert" };
private List<ChatMessage> Messages = new();
private List<UserModel> TeamMembers = new()
{
new UserModel { ID = "user2", User = "Michale Suyama" },
new UserModel { ID = "user3", User = "Reena" },
new UserModel { ID = "user4", User = "Janet" }
};
private void OnMentionSelecting(MentionValueSelectingEventArgs<UserModel> args)
{
// Log mention with timestamp
var mention = new
{
MentionedUser = args.ItemData.User,
MentionedBy = CurrentUser.User,
Timestamp = DateTime.Now
};
Console.WriteLine($"[{mention.Timestamp:HH:mm:ss}] {mention.MentionedBy} mentioned @{mention.MentionedUser}");
// Could store in database for analytics
LogMention(mention);
}
private void LogMention(object mention)
{
// Store mention data for analytics
}
}Complete Example with All Events
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 500px; width: 600px;">
<SfChatUI
ID="chatUser"
User="CurrentUser"
Messages="Messages"
MentionChar="@"
MentionUsers="@TeamMembers"
Created="@OnChatCreated"
MessageSend="@OnMessageSend"
UserTyping="@OnUserTyping"
ValueSelecting="@OnMentionSelecting"
AttachmentClick="@OnAttachmentClick"
OnAttachmentUploadReady="@OnUploadReady"
AttachmentUploadSuccess="@OnUploadSuccess"
AttachmentUploadFailed="@OnUploadFailed"
AttachmentRemoved="@OnAttachmentRemoved">
<ChatUIAttachment
Enable="true"
SaveUrl="@SaveUrl"
RemoveUrl="@RemoveUrl"
AllowedFileTypes=".pdf,.docx,.jpg,.png"
MaxFileSize="5000000">
</ChatUIAttachment>
</SfChatUI>
</div>
@code {
private string SaveUrl = "api/upload/save";
private string RemoveUrl = "api/upload/remove";
private UserModel CurrentUser = new UserModel
{
ID = "user1",
User = "Albert"
};
private List<ChatMessage> Messages = new();
private List<UserModel> TeamMembers = new()
{
new UserModel { ID = "user2", User = "Michale Suyama" },
new UserModel { ID = "user3", User = "Reena" }
};
// Lifecycle Events
private void OnChatCreated()
{
Console.WriteLine("Chat UI created");
}
// Message Events
private void OnMessageSend(ChatMessageSendEventArgs args)
{
Console.WriteLine($"Message sent: {args.Message.Text}");
}
private void OnUserTyping(ChatUserTypingEventArgs args)
{
Console.WriteLine($"User typing: {args.IsTyping}");
}
// Mention Events
private void OnMentionSelecting(MentionValueSelectingEventArgs<UserModel> args)
{
Console.WriteLine($"User mentioned: @{args.ItemData.User}");
}
// Attachment Events
private void OnAttachmentClick(ChatAttachmentClickEventArgs args)
{
Console.WriteLine($"Attachment clicked: {args.SelectedFile?.Name}");
}
private void OnUploadReady(AttachmentUploadReadyEventArgs args)
{
Console.WriteLine("Upload starting");
}
private void OnUploadSuccess(SuccessEventArgs args)
{
Console.WriteLine("File uploaded successfully");
}
private void OnUploadFailed(FailureEventArgs args)
{
Console.WriteLine("File upload failed");
}
private void OnAttachmentRemoved(RemovingEventArgs args)
{
Console.WriteLine("Attachment removed");
}
}Event Summary Table
| Event Category | Event Name | Event Args Type | When It Fires |
|---|---|---|---|
| Lifecycle | Created | object | Component initialized |
| Messages | MessageSend | ChatMessageSendEventArgs | Message being sent |
| Messages | UserTyping | ChatUserTypingEventArgs | User typing in input |
| Mentions | ValueSelecting | MentionValueSelectingEventArgs<UserModel> | User selects a mention |
| Attachments | OnAttachmentUploadReady | AttachmentUploadReadyEventArgs | Before upload starts |
| Attachments | AttachmentUploadSuccess | SuccessEventArgs | Upload successful |
| Attachments | AttachmentUploadFailed | FailureEventArgs | Upload failed |
| Attachments | AttachmentClick | ChatAttachmentClickEventArgs | Attachment clicked |
| Attachments | AttachmentRemoved | RemovingEventArgs | Attachment removed |
Best Practices
1. Always Set URLs - Configure both SaveUrl and RemoveUrl for attachments 2. File Validation - Validate file type and size on both client and server 3. Security - Sanitize file names and restrict upload paths 4. Error Handling - Provide clear error messages to users for all events 5. Size Limits - Set reasonable MaxFileSize values based on your use case 6. File Organization - Store files in organized server directories 7. Logging - Log file uploads and user actions for audit trail 8. Cleanup - Implement cleanup for orphaned/old files 9. Event Cancellation - Use args.Cancel = true to prevent unwanted actions 10. Mention Notifications - Send notifications when users are mentioned 11. Track User Activity - Use events to track engagement and analytics 12. Validate Mentions - Ensure mentioned users are valid and allowed
Getting Started with Chat UI
Installation
NuGet Package Setup
Install the Syncfusion.Blazor.InteractiveChat NuGet package:
dotnet add package Syncfusion.Blazor.InteractiveChatImport Namespaces
Add the required using statements to your _Imports.razor file:
@using Syncfusion.Blazor
@using Syncfusion.Blazor.InteractiveChatRegister Service
In your Program.cs, add the Syncfusion Blazor service registration:
// Program.cs
builder.Services.AddSyncfusionBlazor();Add Theme
Include the Syncfusion Blazor theme stylesheet in your App.razor or layout file:
<!-- Bootstrap 5 theme (or choose another: material, fluent, tailwind) -->
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />
<!-- Syncfusion Blazor script -->
<script src="_content/Syncfusion.Blazor.Core/scripts/syncfusion-blazor.min.js"></script>Available themes: bootstrap5.css, material.css, fluent.css, tailwind.css, fabric.css, material-dark.css
Basic Component Structure
Minimal Example
Create a simple chat UI with two users:
@page "/chat"
@using Syncfusion.Blazor.InteractiveChat
<h2>Chat Application</h2>
<div style="height: 500px; width: 600px;">
<SfChatUI ID="chatUser" User="CurrentUserModel" Messages="ChatMessages"></SfChatUI>
</div>
@code {
private UserModel CurrentUserModel = new UserModel
{
ID = "User1",
User = "Albert"
};
private UserModel OtherUserModel = new UserModel
{
ID = "User2",
User = "Michale Suyama"
};
private List<ChatMessage> ChatMessages = new()
{
new ChatMessage {
Text = "Hi, thinking of painting this weekend.",
Author = new UserModel { ID = "User1", User = "Albert" }
},
new ChatMessage {
Text = "That's fun! What will you paint?",
Author = new UserModel { ID = "User2", User = "Michale Suyama" }
},
new ChatMessage {
Text = "Maybe landscapes.",
Author = new UserModel { ID = "User1", User = "Albert" }
}
};
}Component Properties
The SfChatUI component has several key properties for configuration:
- ID: Unique component identifier
- User: Current logged-in user (UserModel)
- Messages: Collection of ChatMessage objects
- ShowTimestamp: Display timestamps on messages
- TypingUsers: List of users currently typing
- ShowTimeBreak: Display date separators between messages
UserModel Setup
Define users with complete information:
private UserModel CreateUser(string id, string name)
{
return new UserModel
{
ID = id,
User = name,
AvatarBgColor = "#4a90e2",
StatusIconCss = "e-icons e-user-online"
};
}UserModel Properties:
- ID: Unique user identifier
- User: Display name
- AvatarUrl: Image URL for avatar
- AvatarBgColor: Background color for avatar
- StatusIconCss: CSS class for status indicator
- CssClass: Custom styling class
ChatMessage Setup
Create messages with full context:
private ChatMessage CreateMessage(string text, UserModel author)
{
return new ChatMessage
{
Text = text,
Author = author,
Timestamp = DateTime.Now
};
}ChatMessage Properties:
- Text: Message content (supports HTML)
- Author: UserModel who sent the message
- Timestamp: Date/time the message was sent
- ID: Unique message identifier (auto-generated)
Container Styling
Set appropriate dimensions for the component container:
<!-- Fixed height with scroll -->
<div style="height: 500px; width: 100%; overflow-y: auto;">
<SfChatUI ID="chatUser" User="CurrentUser" Messages="Messages"></SfChatUI>
</div>
<!-- Flex layout for responsive sizing -->
<div style="display: flex; flex-direction: column; height: 100vh;">
<div style="flex: 1; overflow-y: auto;">
<SfChatUI ID="chatUser" User="CurrentUser" Messages="Messages"></SfChatUI>
</div>
</div>Basic Configuration
A complete minimal setup with common properties:
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 500px; width: 600px;">
<SfChatUI
ID="myChat"
User="CurrentUser"
Messages="ChatMessages"
ShowTimestamp="true"
TimestampFormat="hh:mm tt">
</SfChatUI>
</div>
@code {
private UserModel CurrentUser = new UserModel
{
ID = "user1",
User = "Albert",
AvatarBgColor = "#4a90e2"
};
private UserModel OtherUser = new UserModel
{
ID = "user2",
User = "Michale",
AvatarBgColor = "#7cb342"
};
private List<ChatMessage> ChatMessages = new()
{
new ChatMessage
{
Text = "Hello! Welcome to the chat.",
Author = CurrentUser,
Timestamp = DateTime.Now.AddHours(-2)
},
new ChatMessage
{
Text = "Thanks! Glad to be here.",
Author = OtherUser,
Timestamp = DateTime.Now.AddHours(-1)
},
new ChatMessage
{
Text = "Let's get started then!",
Author = CurrentUser,
Timestamp = DateTime.Now
}
};
}Managing Messages Dynamically
Add new messages to the conversation:
private async Task SendMessage(string text)
{
var newMessage = new ChatMessage
{
Text = text,
Author = CurrentUser,
Timestamp = DateTime.Now
};
ChatMessages.Add(newMessage);
StateHasChanged();
// Simulate other user response
await Task.Delay(1000);
var response = new ChatMessage
{
Text = "Thanks for your message!",
Author = OtherUser,
Timestamp = DateTime.Now
};
ChatMessages.Add(response);
StateHasChanged();
}Next Steps
- Message Management: Configure and manage message content
- User Profiles: Set up avatars and user information
- Typing Indicators: Show when users are typing
- Timestamps: Configure time and date formatting
- Templates: Customize message appearance
- Attachments: Enable file sharing
- Events: Handle user interactions
Messages & Users Management
Table of Contents
Message Configuration
The Blazor Chat UI component manages messages through the Messages property. Each message is a ChatMessage object representing a single message in the conversation.
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 600px;">
<SfChatUI ID="chatUser" User="CurrentUserModel" Messages="ChatUserMessages"></SfChatUI>
</div>
@code {
private List<ChatMessage> ChatUserMessages = new()
{
new ChatMessage()
{
Text = "Hi, thinking of painting this weekend.",
Author = new UserModel { ID = "User1", User = "Albert" }
},
new ChatMessage()
{
Text = "That's fun! What will you paint?",
Author = new UserModel { ID = "User2", User = "Michale Suyama" }
},
new ChatMessage()
{
Text = "Maybe landscapes.",
Author = new UserModel { ID = "User1", User = "Albert" }
}
};
}User Model Setup
Defining the Current User
The User property specifies the currently logged-in user. This is essential for identifying which messages belong to the current user (displayed on the right) versus other users (displayed on the left).
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 600px;">
<SfChatUI ID="chatUser" User="CurrentUserModel" Messages="ChatUserMessages"></SfChatUI>
</div>
@code {
private UserModel CurrentUserModel = new UserModel()
{
ID = "User1",
User = "Albert"
};
private List<ChatMessage> ChatUserMessages = new()
{
new ChatMessage()
{
Text = "Hi, thinking of painting this weekend.",
Author = new UserModel { ID = "User1", User = "Albert" }
},
new ChatMessage()
{
Text = "That's fun! What will you paint?",
Author = new UserModel { ID = "User2", User = "Michale Suyama" }
}
};
}Key UserModel Properties:
- ID: Unique identifier for the user (required for matching with message authors)
- User: Display name shown in the interface
- AvatarUrl: Optional image URL for avatar
- AvatarBgColor: Background color for avatar initials
- StatusIconCss: CSS class for status indicator
- CssClass: Custom CSS styling
Creating Multiple Users
Define different users for a multi-user conversation:
private UserModel AlbertUser = new UserModel()
{
ID = "user1",
User = "Albert",
AvatarBgColor = "#4a90e2"
};
private UserModel MichaleUser = new UserModel()
{
ID = "user2",
User = "Michale Suyama",
AvatarBgColor = "#7cb342"
};
private UserModel ReenaUser = new UserModel()
{
ID = "user3",
User = "Reena",
AvatarBgColor = "#e84c3d"
};Message Text Content
Plain Text Messages
Messages can contain plain text content:
new ChatMessage()
{
Text = "This is a plain text message.",
Author = CurrentUserModel
}HTML-Formatted Messages
Messages support HTML content for rich formatting:
new ChatMessage()
{
Text = "<div><strong>Important:</strong> Please review the document.</div>",
Author = CurrentUserModel
}When rendering HTML messages in templates, use @((MarkupString)context.Message.Text) to prevent escaping:
<SfChatUI ID="chat" User="CurrentUser" Messages="Messages">
<MessageTemplate>
<div>@((MarkupString)context.Message.Text)</div>
</MessageTemplate>
</SfChatUI>Message with Special Formatting
Create formatted messages with structure:
private ChatMessage CreateFormattedMessage(string title, string content, UserModel author)
{
var html = $@"
<div style='border-left: 3px solid #4a90e2; padding: 8px;'>
<div style='font-weight: bold; color: #333;'>{title}</div>
<div style='color: #666; margin-top: 4px;'>{content}</div>
</div>";
return new ChatMessage { Text = html, Author = author };
}Author Identification
The Author property identifies the sender of each message. The component compares the author ID with the current user ID to determine message alignment.
Message Alignment Logic
// Messages from the current user appear on the right
if (message.Author.ID == CurrentUser.ID)
{
// Right-aligned (current user message)
}
else
{
// Left-aligned (other user message)
}Creating Messages with Authors
private ChatMessage CreateMessage(string text, UserModel sender)
{
return new ChatMessage
{
Text = text,
Author = sender // Identifies the sender
};
}
// Usage
var albertMessage = CreateMessage("Hello!", AlbertUser);
var michaleMessage = CreateMessage("Hi there!", MichaleUser);Message Collections
Initializing Message Collection
Load pre-existing messages into the component:
private List<ChatMessage> ChatUserMessages = new()
{
new ChatMessage()
{
Text = "Hi, thinking of painting this weekend.",
Author = new UserModel { ID = "User1", User = "Albert" }
},
new ChatMessage()
{
Text = "That's fun! What will you paint?",
Author = new UserModel { ID = "User2", User = "Michale Suyama" }
},
new ChatMessage()
{
Text = "Maybe landscapes.",
Author = new UserModel { ID = "User1", User = "Albert" }
}
};Adding Messages Dynamically
Add new messages to the collection during conversation:
private async Task AddNewMessage(string text, UserModel author)
{
var newMessage = new ChatMessage
{
Text = text,
Author = author,
Timestamp = DateTime.Now
};
ChatUserMessages.Add(newMessage);
StateHasChanged();
}Clearing Message History
Remove all messages from the conversation:
private void ClearConversation()
{
ChatUserMessages.Clear();
StateHasChanged();
}Message History with Pagination
Load messages in batches for performance:
private async Task LoadMoreMessages()
{
// Fetch older messages from database
var olderMessages = await FetchOlderMessages(lastMessageId: ChatUserMessages.First().ID);
// Prepend to collection
ChatUserMessages.InsertRange(0, olderMessages);
StateHasChanged();
}
private async Task<List<ChatMessage>> FetchOlderMessages(string lastMessageId)
{
// Database query for messages before the first current message
return await Database.GetMessagesBeforeId(lastMessageId);
}Message Count and Statistics
Track conversation statistics:
private int GetTotalMessageCount()
{
return ChatUserMessages.Count;
}
private int GetMessagesFromUser(UserModel user)
{
return ChatUserMessages.Count(m => m.Author.ID == user.ID);
}
private DateTime GetLastMessageTime()
{
return ChatUserMessages.LastOrDefault()?.Timestamp ?? DateTime.MinValue;
}Best Practices
1. Always assign Author - Every message must have an Author 2. Match Author ID to Current User - Ensure author IDs match the User ID for correct alignment 3. Use Consistent User Objects - Reuse UserModel instances across messages 4. HTML Sanitization - Sanitize user-generated HTML for security 5. Timestamp Management - Always set message timestamps 6. Message Immutability - Avoid modifying message content after creation 7. Collection Size - For large conversations, implement pagination 8. Error Handling - Handle null authors and invalid messages gracefully
Programmatic API & Methods
Table of Contents
Component Reference
To use programmatic methods, you need a reference to the SfChatUI component:
@using Syncfusion.Blazor.InteractiveChat
<SfChatUI ID="chat" User="CurrentUser" Messages="Messages"></SfChatUI>
@code {
private UserModel CurrentUser = new UserModel
{
ID = "user1",
User = "Albert"
};
private List<ChatMessage> Messages = new();
}---
ScrollToBottomAsync
Method Signature: Task ScrollToBottomAsync()
Asynchronously scrolls the chat UI to the bottom, showing the latest messages.
Use Cases
- Auto-scroll when new messages arrive
- "Jump to latest" button functionality
- Reset scroll position after loading history
- Scroll after programmatically adding messages
Basic Usage
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages"></SfChatUI>
<button @onclick="JumpToLatest">Jump to Latest Messages</button>
@code {
private SfChatUI chatRef;
private async Task JumpToLatest()
{
await chatRef.ScrollToBottomAsync();
}
}Pattern 1: Auto-Scroll on New Message
<SfChatUI
@ref="chatRef"
ID="chat"
User="CurrentUser"
Messages="Messages"
MessageSend="@OnMessageSend">
</SfChatUI>
@code {
private SfChatUI chatRef;
private List<ChatMessage> Messages = new();
private async Task OnMessageSend(ChatMessageSendEventArgs args)
{
// Add message to collection
Messages.Add(args.Message);
// Auto-scroll to show the new message
await chatRef.ScrollToBottomAsync();
StateHasChanged();
}
}Pattern 2: Scroll After Loading History
<button @onclick="LoadMessages">Load Messages</button>
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages"></SfChatUI>
@code {
private SfChatUI chatRef;
private List<ChatMessage> Messages = new();
private async Task LoadMessages()
{
// Load messages from database
Messages = await LoadMessagesFromDatabase();
StateHasChanged();
// Scroll to bottom to show latest
await Task.Delay(100); // Allow rendering
await chatRef.ScrollToBottomAsync();
}
private async Task<List<ChatMessage>> LoadMessagesFromDatabase()
{
// Database query simulation
await Task.Delay(500);
return new List<ChatMessage>
{
new ChatMessage { Text = "Message 1", Author = CurrentUser },
new ChatMessage { Text = "Message 2", Author = CurrentUser }
};
}
}Pattern 3: Conditional Auto-Scroll
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages"></SfChatUI>
@code {
private SfChatUI chatRef;
private bool autoScrollEnabled = true;
private async Task AddNewMessage(ChatMessage message)
{
Messages.Add(message);
StateHasChanged();
// Only auto-scroll if enabled
if (autoScrollEnabled)
{
await chatRef.ScrollToBottomAsync();
}
}
}---
ScrollToMessageAsync
Method Signature: Task ScrollToMessageAsync(string messageId)
Asynchronously scrolls to a specific message by its ID.
Use Cases
- Navigate to search results
- Jump to mentioned message
- Highlight specific conversation point
- Navigate to quoted/replied message
Basic Usage
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages"></SfChatUI>
<button @onclick="@(() => GoToMessage("msg-123"))">Go to Message</button>
@code {
private SfChatUI chatRef;
private async Task GoToMessage(string messageId)
{
await chatRef.ScrollToMessageAsync(messageId);
}
}Pattern 1: Search and Navigate
<input @bind="searchText" placeholder="Search messages" />
<button @onclick="SearchAndNavigate">Search</button>
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages"></SfChatUI>
@code {
private SfChatUI chatRef;
private string searchText = "";
private List<ChatMessage> Messages = new();
private async Task SearchAndNavigate()
{
// Find first matching message
var foundMessage = Messages.FirstOrDefault(m =>
m.Text.Contains(searchText, StringComparison.OrdinalIgnoreCase));
if (foundMessage != null)
{
await chatRef.ScrollToMessageAsync(foundMessage.ID);
}
else
{
Console.WriteLine("Message not found");
}
}
}Pattern 2: Navigate to Quoted Message
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages">
<MessageTemplate>
<div>
@if (!string.IsNullOrEmpty(context.Message.QuotedMessageId))
{
<div class="quoted-message" @onclick="@(() => NavigateToQuoted(context.Message.QuotedMessageId))">
<small>View quoted message</small>
</div>
}
<div>@context.Message.Text</div>
</div>
</MessageTemplate>
</SfChatUI>
@code {
private SfChatUI chatRef;
private async Task NavigateToQuoted(string quotedMessageId)
{
await chatRef.ScrollToMessageAsync(quotedMessageId);
}
}Pattern 3: Navigate from Notification
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages"></SfChatUI>
@code {
private SfChatUI chatRef;
protected override async Task OnInitializedAsync()
{
// Check if navigating from notification
var messageId = NavigationManager.QueryString("messageId");
if (!string.IsNullOrEmpty(messageId))
{
// Load messages first
await LoadMessages();
// Then scroll to specific message
await Task.Delay(200); // Allow rendering
await chatRef.ScrollToMessageAsync(messageId);
}
}
}---
UpdateMessageAsync
Method Signature: Task UpdateMessageAsync(ChatMessage message, string msgId)
Updates an existing message in the chat UI.
Use Cases
- Edit sent messages
- Update message status (read/delivered)
- Correct typos or errors
- Update message content from backend
Basic Usage
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages"></SfChatUI>
@code {
private SfChatUI chatRef;
private async Task EditMessage(string messageId, string newText)
{
var message = Messages.FirstOrDefault(m => m.ID == messageId);
if (message != null)
{
message.Text = newText;
await chatRef.UpdateMessageAsync(message, messageId);
}
}
}Pattern 1: Message Editing UI
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages">
<MessageTemplate>
<div>
<p>@context.Message.Text</p>
@if (context.Message.Author.ID == CurrentUser.ID)
{
<button @onclick="@(() => StartEdit(context.Message))">Edit</button>
}
</div>
</MessageTemplate>
</SfChatUI>
@if (editingMessage != null)
{
<div class="edit-panel">
<input @bind="editText" />
<button @onclick="SaveEdit">Save</button>
<button @onclick="CancelEdit">Cancel</button>
</div>
}
@code {
private SfChatUI chatRef;
private ChatMessage editingMessage;
private string editText;
private void StartEdit(ChatMessage message)
{
editingMessage = message;
editText = message.Text;
}
private async Task SaveEdit()
{
if (editingMessage != null)
{
editingMessage.Text = editText;
await chatRef.UpdateMessageAsync(editingMessage, editingMessage.ID);
editingMessage = null;
editText = "";
}
}
private void CancelEdit()
{
editingMessage = null;
editText = "";
}
}Pattern 2: Real-Time Message Updates
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages"></SfChatUI>
@code {
private SfChatUI chatRef;
protected override async Task OnInitializedAsync()
{
// Subscribe to real-time updates
await hubConnection.On<string, string>("MessageUpdated", async (messageId, newText) =>
{
var message = Messages.FirstOrDefault(m => m.ID == messageId);
if (message != null)
{
message.Text = newText;
await chatRef.UpdateMessageAsync(message, messageId);
StateHasChanged();
}
});
}
}Pattern 3: Update Message Status
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages">
<MessageTemplate>
<div>
<p>@context.Message.Text</p>
<small>@context.Message.Status</small>
</div>
</MessageTemplate>
</SfChatUI>
@code {
private SfChatUI chatRef;
private async Task MarkAsRead(string messageId)
{
var message = Messages.FirstOrDefault(m => m.ID == messageId);
if (message != null)
{
message.Status = "Read";
await chatRef.UpdateMessageAsync(message, messageId);
}
}
}Pattern 4: Batch Message Updates
<button @onclick="MarkAllAsRead">Mark All as Read</button>
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages"></SfChatUI>
@code {
private SfChatUI chatRef;
private async Task MarkAllAsRead()
{
foreach (var message in Messages.Where(m => m.Status == "Unread"))
{
message.Status = "Read";
await chatRef.UpdateMessageAsync(message, message.ID);
}
StateHasChanged();
}
}---
FocusAsync
Method Signature: Task FocusAsync()
Asynchronously sets focus on the chat input text area.
Use Cases
- Focus input after page load
- Return focus after modal close
- Keyboard navigation support
- Quick reply functionality
Basic Usage
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages"></SfChatUI>
<button @onclick="FocusInput">Focus Chat Input</button>
@code {
private SfChatUI chatRef;
private async Task FocusInput()
{
await chatRef.FocusAsync();
}
}Pattern 1: Auto-Focus on Page Load
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages"></SfChatUI>
@code {
private SfChatUI chatRef;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// Auto-focus input when page loads
await Task.Delay(100); // Allow component initialization
await chatRef.FocusAsync();
}
}
}Pattern 2: Focus After Quick Reply
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages"></SfChatUI>
<div class="quick-replies">
<button @onclick="@(() => SendQuickReply("Yes"))">Yes</button>
<button @onclick="@(() => SendQuickReply("No"))">No</button>
<button @onclick="@(() => SendQuickReply("Maybe"))">Maybe</button>
</div>
@code {
private SfChatUI chatRef;
private async Task SendQuickReply(string reply)
{
// Add quick reply message
Messages.Add(new ChatMessage
{
Text = reply,
Author = CurrentUser
});
StateHasChanged();
// Return focus to input for follow-up
await chatRef.FocusAsync();
}
}Pattern 3: Focus After Modal Close
<SfChatUI @ref="chatRef" ID="chat" User="CurrentUser" Messages="Messages"></SfChatUI>
@if (showModal)
{
<div class="modal">
<h3>Attachment Options</h3>
<button @onclick="CloseModal">Close</button>
</div>
}
@code {
private SfChatUI chatRef;
private bool showModal = false;
private async Task CloseModal()
{
showModal = false;
StateHasChanged();
// Return focus to chat input
await chatRef.FocusAsync();
}
}---
Complete Example: All Methods Combined
@page "/advanced-chat"
@using Syncfusion.Blazor.InteractiveChat
<h3>Advanced Chat with Programmatic Control</h3>
<div class="toolbar">
<button @onclick="ScrollToBottom">Latest Messages</button>
<button @onclick="FocusInput">Focus Input</button>
<button @onclick="EnableEditMode">Edit Last Message</button>
<input @bind="searchText" placeholder="Search..." />
<button @onclick="SearchMessages">Search</button>
</div>
<div style="height: 500px; width: 600px;">
<SfChatUI
@ref="chatRef"
ID="chat"
User="CurrentUser"
Messages="Messages"
MessageSend="@OnMessageSend">
</SfChatUI>
</div>
@if (isEditing)
{
<div class="edit-panel">
<input @bind="editText" />
<button @onclick="SaveEdit">Save Edit</button>
<button @onclick="CancelEdit">Cancel</button>
</div>
}
@code {
private SfChatUI chatRef;
private string searchText = "";
private bool isEditing = false;
private string editText = "";
private ChatMessage editingMessage;
private UserModel CurrentUser = new UserModel
{
ID = "user1",
User = "Albert",
AvatarBgColor = "#4a90e2"
};
private List<ChatMessage> Messages = new()
{
new ChatMessage
{
ID = "msg1",
Text = "Welcome to advanced chat!",
Author = new UserModel { ID = "user1", User = "Albert" }
}
};
// ScrollToBottomAsync example
private async Task ScrollToBottom()
{
await chatRef.ScrollToBottomAsync();
}
// FocusAsync example
private async Task FocusInput()
{
await chatRef.FocusAsync();
}
// UpdateMessageAsync example
private void EnableEditMode()
{
editingMessage = Messages.LastOrDefault(m => m.Author.ID == CurrentUser.ID);
if (editingMessage != null)
{
editText = editingMessage.Text;
isEditing = true;
}
}
private async Task SaveEdit()
{
if (editingMessage != null)
{
editingMessage.Text = editText;
await chatRef.UpdateMessageAsync(editingMessage, editingMessage.ID);
isEditing = false;
}
}
private void CancelEdit()
{
isEditing = false;
editText = "";
}
// ScrollToMessageAsync example
private async Task SearchMessages()
{
var found = Messages.FirstOrDefault(m =>
m.Text.Contains(searchText, StringComparison.OrdinalIgnoreCase));
if (found != null)
{
await chatRef.ScrollToMessageAsync(found.ID);
}
}
private async Task OnMessageSend(ChatMessageSendEventArgs args)
{
Messages.Add(args.Message);
await chatRef.ScrollToBottomAsync();
StateHasChanged();
}
}---
Best Practices
1. Always await method calls - All methods are async and return Task 2. Check for null reference - Ensure @ref is initialized before calling methods 3. Allow rendering time - Use Task.Delay() when calling methods immediately after state changes 4. Handle errors gracefully - Wrap calls in try-catch for production code 5. Update state after modifications - Call StateHasChanged() after updating Messages collection 6. Use component reference - Always use @ref to get component instance 7. Combine with events - Use methods in event handlers for dynamic behavior 8. Consider timing - Call methods after OnAfterRenderAsync for initialization scenarios
---
Error Handling
private async Task SafeScrollToBottom()
{
try
{
if (chatRef != null)
{
await chatRef.ScrollToBottomAsync();
}
}
catch (Exception ex)
{
Console.WriteLine($"Scroll error: {ex.Message}");
}
}Templates & Customization
Table of Contents
- Empty Chat Template
- Message Template
- TimeBreak Template
- Typing Users Template
- Suggestion Template
- CSS Styling
Empty Chat Template
The EmptyChatTemplate customizes the chat interface when no messages are displayed. This creates an engaging initial experience for users starting a conversation.
Basic Empty Chat Template
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 600px;">
<SfChatUI ID="chatUser" User="CurrentUserModel">
<EmptyChatTemplate>
<div class="empty-chat-text">
<h4><span class="e-icons e-comment-show"></span></h4>
<h4>No Messages Yet</h4>
<p>Start a conversation to see your messages here.</p>
</div>
</EmptyChatTemplate>
</SfChatUI>
</div>
<style>
.empty-chat-text {
font-size: 15px;
text-align: center;
margin-top: 90px;
}
</style>
@code {
private UserModel CurrentUserModel = new UserModel()
{
ID = "User1",
User = "Albert"
};
}Custom Welcome Template
<SfChatUI ID="chat" User="CurrentUser">
<EmptyChatTemplate>
<div class="welcome-template">
<div class="welcome-header">
<h2>Welcome! 👋</h2>
<p>Start chatting with your friends</p>
</div>
<div class="welcome-suggestions">
<div class="suggestion-item">
<span class="icon">📝</span>
<p>Send your first message</p>
</div>
<div class="suggestion-item">
<span class="icon">👥</span>
<p>Add more participants</p>
</div>
<div class="suggestion-item">
<span class="icon">📎</span>
<p>Share files and media</p>
</div>
</div>
</div>
</EmptyChatTemplate>
</SfChatUI>
<style>
.welcome-template {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
}
.welcome-header h2 {
margin: 0 0 10px 0;
}
.welcome-suggestions {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-top: 40px;
width: 100%;
}
.suggestion-item {
text-align: center;
}
.suggestion-item .icon {
font-size: 32px;
display: block;
margin-bottom: 8px;
}
</style>Message Template
The MessageTemplate customizes the appearance and styling of each message. The template context includes Message and Index.
Basic Custom Message Template
@using Syncfusion.Blazor.InteractiveChat
<div class="template-chatui" style="height: 400px; width: 600px;">
<SfChatUI ID="chatUser" User="CurrentUserModel" Messages="ChatUserMessages">
<MessageTemplate>
<div class="message-items e-card">
<div class="message-text">@((MarkupString)context.Message.Text)</div>
</div>
</MessageTemplate>
</SfChatUI>
</div>
@code {
private UserModel CurrentUserModel = new UserModel()
{
ID = "User1",
User = "Albert"
};
private UserModel MichaleUserModel = new UserModel()
{
ID = "User2",
User = "Michale Suyama"
};
private List<ChatMessage> ChatUserMessages = new()
{
new ChatMessage()
{
Text = "Hi, thinking of painting this weekend.",
Author = CurrentUserModel
},
new ChatMessage()
{
Text = "That's fun! What will you paint?",
Author = MichaleUserModel
}
};
}
<style>
.template-chatui .e-right .message-items {
border-radius: 16px 16px 2px 16px;
background-color: #c5ffbf;
}
.template-chatui .e-left .message-items {
border-radius: 16px 16px 16px 2px;
background-color: #f5f5f5;
}
.template-chatui .message-items {
padding: 8px 12px;
max-width: 70%;
}
.message-text {
word-wrap: break-word;
}
</style>Advanced Message Template with Metadata
<MessageTemplate>
<div class="custom-message">
<div class="message-header">
<strong>@context.Message.Author.User</strong>
<span class="message-time">@context.Message.Timestamp?.ToString("hh:mm tt")</span>
</div>
<div class="message-content">
@((MarkupString)context.Message.Text)
</div>
<div class="message-footer">
<span class="message-index">#@context.Index</span>
</div>
</div>
</MessageTemplate>
<style>
.custom-message {
padding: 8px 12px;
border-radius: 8px;
background: #f5f5f5;
}
.message-header {
display: flex;
justify-content: space-between;
font-size: 12px;
margin-bottom: 4px;
}
.message-time {
color: #999;
font-size: 11px;
}
.message-content {
margin: 4px 0;
}
.message-footer {
font-size: 10px;
color: #ccc;
margin-top: 4px;
}
</style>TimeBreak Template
Customize date separators between message groups using TimeBreakTemplate. Template context includes MessageDate.
Basic TimeBreak Template
<SfChatUI ID="chat" User="CurrentUser" Messages="Messages" ShowTimeBreak="true">
<TimeBreakTemplate>
<div class="timebreak-wrapper">
@(context.MessageDate.Value.ToString("MMMM dd, yyyy"))
</div>
</TimeBreakTemplate>
</SfChatUI>
<style>
.timebreak-wrapper {
background-color: #6495ed;
color: #ffffff;
border-radius: 5px;
padding: 4px 8px;
text-align: center;
margin: 8px 0;
font-size: 12px;
font-weight: 500;
}
</style>Advanced TimeBreak with Relative Dates
<TimeBreakTemplate>
<div class="advanced-timebreak">
<span class="date-label">@GetRelativeDate(context.MessageDate.Value)</span>
</div>
</TimeBreakTemplate>
<style>
.advanced-timebreak {
display: flex;
align-items: center;
gap: 12px;
margin: 16px 0;
}
.advanced-timebreak::before,
.advanced-timebreak::after {
content: '';
flex: 1;
height: 1px;
background: #ddd;
}
.date-label {
background: white;
padding: 0 12px;
color: #666;
font-size: 12px;
font-weight: 600;
white-space: nowrap;
}
</style>
@code {
private string GetRelativeDate(DateTime messageDate)
{
var today = DateTime.Today;
if (messageDate.Date == today)
return "Today";
else if (messageDate.Date == today.AddDays(-1))
return "Yesterday";
else if (messageDate.Date > today.AddDays(-7))
return messageDate.ToString("dddd");
else
return messageDate.ToString("MMMM dd");
}
}Typing Users Template
Customize the typing indicator using TypingUsersTemplate. Template context includes Users.
Basic Typing Template
<SfChatUI ID="chat" User="CurrentUser" Messages="Messages" TypingUsers="TypingUsers">
<TypingUsersTemplate>
<div class="typing-wrapper">
@for (int i = 0; i < context.Users.Count; i++)
{
if (i == context.Users.Count - 1 && i > 0)
{
<span>and </span>
}
<span class="typing-user">@context.Users[i].User</span>
}
<span> @(context.Users.Count == 1 ? "is" : "are") typing...</span>
</div>
</TypingUsersTemplate>
</SfChatUI>
<style>
.typing-wrapper {
display: flex;
gap: 4px;
align-items: center;
font-family: Arial, sans-serif;
font-size: 13px;
color: #666;
margin: 4px 0;
}
.typing-user {
font-weight: 600;
color: #0078d4;
}
</style>Advanced Typing with Animation
<TypingUsersTemplate>
<div class="advanced-typing">
<div class="typing-avatars">
@foreach (var user in context.Users)
{
<div class="typing-avatar">@user.User[0]</div>
}
</div>
<div class="typing-indicator">
<span></span><span></span><span></span>
</div>
</div>
</TypingUsersTemplate>
<style>
.advanced-typing {
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
}
.typing-avatars {
display: flex;
margin-left: -8px;
}
.typing-avatar {
width: 28px;
height: 28px;
border-radius: 50%;
background: #4a90e2;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
margin-left: -8px;
border: 2px solid white;
}
.typing-indicator {
display: flex;
gap: 3px;
margin-left: 4px;
}
.typing-indicator span {
width: 6px;
height: 6px;
border-radius: 50%;
background: #999;
animation: typing 1.4s infinite;
}
.typing-indicator span:nth-child(2) {
animation-delay: 0.2s;
}
.typing-indicator span:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes typing {
0%, 60%, 100% {
opacity: 0.5;
}
30% {
opacity: 1;
}
}
</style>Suggestion Template
Customize quick reply suggestions using SuggestionTemplate.
Basic Suggestion Template
<SfChatUI ID="chat" User="CurrentUser" Messages="Messages">
<SuggestionTemplate>
<button class="suggestion-btn">
@context.Suggestion
</button>
</SuggestionTemplate>
</SfChatUI>
<style>
.suggestion-btn {
background: #f0f0f0;
border: 1px solid #ddd;
padding: 8px 12px;
border-radius: 4px;
margin: 4px;
cursor: pointer;
transition: all 0.3s ease;
}
.suggestion-btn:hover {
background: #e0e0e0;
border-color: #999;
}
</style>CSS Styling
Component-Wide Styling
<style>
/* Chat container */
.e-chat-ui {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #ffffff;
}
/* Message area */
.e-chat-ui .e-chat-message-area {
background: #f9f9f9;
padding: 12px;
}
/* Left messages (other users) */
.e-chat-ui .e-left .e-message-box {
background: #e3e3e3;
color: #333;
}
/* Right messages (current user) */
.e-chat-ui .e-right .e-message-box {
background: #4a90e2;
color: white;
}
/* Message text */
.e-chat-ui .e-message-text {
word-wrap: break-word;
max-width: 100%;
}
/* Avatar */
.e-chat-ui .e-message-icon {
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
</style>Theme Integration
<!-- Material theme -->
<link href="_content/Syncfusion.Blazor.Themes/material.css" rel="stylesheet" />
<!-- Bootstrap theme -->
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />
<!-- Fluent theme -->
<link href="_content/Syncfusion.Blazor.Themes/fluent.css" rel="stylesheet" />Best Practices
1. Use MarkupString - Always use @((MarkupString)...) for HTML content 2. Responsive Templates - Design templates that work on mobile and desktop 3. Performance - Keep templates lightweight, avoid complex logic 4. Accessibility - Include alt text and labels in templates 5. Consistent Styling - Maintain consistent colors and spacing 6. Theme Support - Test templates with different themes 7. Error Handling - Handle null or missing data gracefully
Timestamps & Formatting
Table of Contents
- Show/Hide Timestamps
- Timestamp Configuration
- Timestamp Format Customization
- TimeBreak Separators
- TimeBreak Templates
Show/Hide Timestamps
The ShowTimestamp property enables or disables timestamps for all messages. By default, this is set to true, displaying the exact date and time when messages were sent.
Enable Timestamps
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 600px;">
<SfChatUI
ID="chatUser"
User="CurrentUserModel"
ShowTimestamp="true"
Messages="ChatUserMessages">
</SfChatUI>
</div>
@code {
private UserModel CurrentUserModel = new UserModel()
{
ID = "User1",
User = "Albert"
};
private UserModel MichaleUserModel = new UserModel()
{
ID = "User2",
User = "Michale Suyama"
};
private List<ChatMessage> ChatUserMessages = new()
{
new ChatMessage()
{
Text = "Hi, thinking of painting this weekend.",
Author = CurrentUserModel,
Timestamp = new DateTime(2024, 12, 25, 7, 30, 0)
},
new ChatMessage()
{
Text = "That's fun! What will you paint?",
Author = MichaleUserModel,
Timestamp = new DateTime(2024, 12, 25, 8, 0, 0)
},
new ChatMessage()
{
Text = "Maybe landscapes.",
Author = CurrentUserModel,
Timestamp = new DateTime(2024, 12, 25, 11, 0, 0)
}
};
}Disable Timestamps
Hide timestamps for a cleaner interface:
<SfChatUI
ID="chatUser"
User="CurrentUserModel"
ShowTimestamp="false"
Messages="ChatUserMessages">
</SfChatUI>Timestamp Configuration
The Timestamp property on each message specifies the date and time it was sent. By default, it is set to the current date and time when the message is created.
Setting Message Timestamps
// Current time (default)
var message1 = new ChatMessage()
{
Text = "Just sent",
Author = CurrentUser
// Timestamp defaults to DateTime.Now
};
// Specific past time
var message2 = new ChatMessage()
{
Text = "Sent earlier today",
Author = OtherUser,
Timestamp = new DateTime(2024, 12, 25, 7, 30, 0)
};
// Custom timestamp
var message3 = new ChatMessage()
{
Text = "Old conversation",
Author = CurrentUser,
Timestamp = DateTime.Parse("2024-12-20 14:30:00")
};Managing Timestamps During Message Creation
private ChatMessage CreateMessage(string text, UserModel author, DateTime? timestamp = null)
{
return new ChatMessage
{
Text = text,
Author = author,
Timestamp = timestamp ?? DateTime.Now
};
}
// Usage
var messages = new List<ChatMessage>()
{
CreateMessage("First message", User1), // Uses current time
CreateMessage("Second message", User2, new DateTime(2024, 12, 25, 10, 0, 0)),
CreateMessage("Third message", User1) // Uses current time
};Timestamp Format Customization
The TimestampFormat property displays time formats for all messages. The default value is dd/MM/yyyy hh:mm tt.
Common Format Examples
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 600px;">
<SfChatUI
ID="chatUser"
User="CurrentUserModel"
TimestampFormat="@timestampFormat"
Messages="ChatUserMessages">
</SfChatUI>
</div>
@code {
private string timestampFormat = "MMMM hh:mm tt"; // December 2:30 PM
private UserModel CurrentUserModel = new UserModel()
{
ID = "User1",
User = "Albert"
};
private List<ChatMessage> ChatUserMessages = new();
}Format String Options
| Format | Output | Example |
|---|---|---|
dd/MM/yyyy hh:mm tt | Full date with time | 25/12/2024 02:30 PM |
hh:mm tt | Time only | 02:30 PM |
dd/MM/yyyy HH:mm | Full date, 24-hour time | 25/12/2024 14:30 |
MMMM hh:mm tt | Month name with time | December 02:30 PM |
ddd, MMMM dd | Day and date | Tue, December 25 |
h:mm tt | Short time | 2:30 PM |
yyyy-MM-dd HH:mm:ss | ISO format | 2024-12-25 14:30:00 |
Format Examples
// Show only time
<SfChatUI
ID="chat1"
User="CurrentUser"
TimestampFormat="hh:mm tt"
Messages="Messages">
</SfChatUI>
// Show month and time
<SfChatUI
ID="chat2"
User="CurrentUser"
TimestampFormat="MMMM hh:mm tt"
Messages="Messages">
</SfChatUI>
// Show full date and time
<SfChatUI
ID="chat3"
User="CurrentUser"
TimestampFormat="dddd, MMMM dd, yyyy h:mm tt"
Messages="Messages">
</SfChatUI>TimeBreak Separators
The ShowTimeBreak property enables date separators between message groups. When enabled, messages are visually grouped by date with separators showing "Today", "Yesterday", or specific dates.
Enable TimeBreak
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 600px;">
<SfChatUI
ID="chatUser"
User="CurrentUserModel"
ShowTimeBreak="true"
Messages="ChatUserMessages">
</SfChatUI>
</div>
@code {
private UserModel CurrentUserModel = new UserModel()
{
ID = "User1",
User = "Albert"
};
private UserModel MichaleUserModel = new UserModel()
{
ID = "User2",
User = "Michale Suyama"
};
private List<ChatMessage> ChatUserMessages = new()
{
// Yesterday's messages
new ChatMessage()
{
Text = "Good day yesterday!",
Author = MichaleUserModel,
Timestamp = DateTime.Now.AddDays(-1).Date
},
// Today's messages
new ChatMessage()
{
Text = "Hi, thinking of painting this weekend.",
Author = CurrentUserModel,
Timestamp = DateTime.Now.Date.AddHours(7).AddMinutes(30)
},
new ChatMessage()
{
Text = "That's fun! What will you paint?",
Author = MichaleUserModel,
Timestamp = DateTime.Now.Date.AddHours(8)
},
new ChatMessage()
{
Text = "Maybe landscapes.",
Author = CurrentUserModel,
Timestamp = DateTime.Now.Date.AddHours(11)
}
};
}TimeBreak Templates
Customize the appearance of date separators using the TimeBreakTemplate.
Basic TimeBreak Template
@using Syncfusion.Blazor.InteractiveChat
<div class="template-chatui" style="height: 400px; width: 600px;">
<SfChatUI
ID="chatUser"
User="CurrentUserModel"
Messages="ChatUserMessages"
ShowTimeBreak="true">
<TimeBreakTemplate>
<div class="timebreak-wrapper">
@(context.MessageDate.Value.ToString("MMMM dd, yyyy"))
</div>
</TimeBreakTemplate>
</SfChatUI>
</div>
<style>
.template-chatui .timebreak-wrapper {
background-color: #6495ed;
color: #ffffff;
border-radius: 5px;
padding: 2px;
text-align: center;
margin: 8px 0;
}
</style>
@code {
private UserModel CurrentUserModel = new UserModel()
{
ID = "User1",
User = "Albert"
};
private UserModel MichaleUserModel = new UserModel()
{
ID = "User2",
User = "Michale Suyama"
};
private List<ChatMessage> ChatUserMessages = new()
{
new ChatMessage()
{
Text = "Hi, thinking of painting this weekend.",
Author = CurrentUserModel,
Timestamp = new DateTime(2024, 12, 25, 7, 30, 0)
},
new ChatMessage()
{
Text = "That's fun! What will you paint?",
Author = MichaleUserModel,
Timestamp = new DateTime(2024, 12, 25, 8, 0, 0)
},
new ChatMessage()
{
Text = "Maybe landscapes.",
Author = CurrentUserModel,
Timestamp = new DateTime(2024, 12, 25, 11, 0, 0)
}
};
}Advanced TimeBreak with Relative Dates
<SfChatUI
ID="chat"
User="CurrentUser"
Messages="Messages"
ShowTimeBreak="true">
<TimeBreakTemplate>
<div class="advanced-timebreak">
<span class="date-label">@GetRelativeDate(context.MessageDate.Value)</span>
</div>
</TimeBreakTemplate>
</SfChatUI>
<style>
.advanced-timebreak {
display: flex;
align-items: center;
gap: 8px;
margin: 12px 0;
}
.advanced-timebreak::before,
.advanced-timebreak::after {
content: '';
flex: 1;
height: 1px;
background: #ddd;
}
.date-label {
background: white;
padding: 0 8px;
color: #666;
font-size: 12px;
font-weight: 500;
}
</style>
@code {
private string GetRelativeDate(DateTime messageDate)
{
var today = DateTime.Today;
var yesterday = today.AddDays(-1);
var messageDay = messageDate.Date;
if (messageDay == today)
return "Today";
else if (messageDay == yesterday)
return "Yesterday";
else if (messageDay > today.AddDays(-7))
return messageDay.ToString("dddd");
else
return messageDay.ToString("MMMM dd");
}
}Best Practices
1. Consistent Format - Use same timestamp format across the chat 2. Timezone Handling - Consider user timezone when displaying timestamps 3. Relative Dates - Use "Today", "Yesterday" for better UX 4. Performance - Limit TimeBreak calculations for large message lists 5. Accessibility - Ensure timestamp text is readable with sufficient contrast 6. Mobile Optimization - Consider space constraints on mobile devices 7. Internationalization - Format dates based on user's culture/locale
Typing Indicators
Table of Contents
- Show/Hide Typing Indicators
- Multiple Users Typing
- Dynamic Typing Status
- Typing User Management
- Typing Indicator Templates
Show/Hide Typing Indicators
The TypingUsers property displays the current users who are typing to indicate active participants. When the property is empty, typing indicators are removed.
Basic Typing Indicator
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 600px;">
<SfChatUI
ID="chatUser"
User="CurrentUserModel"
TypingUsers="TypingUsers"
Messages="ChatUserMessages">
</SfChatUI>
</div>
@code {
private UserModel CurrentUserModel = new UserModel()
{
ID = "User1",
User = "Albert"
};
private UserModel MichaleUserModel = new UserModel()
{
ID = "User2",
User = "Michale Suyama"
};
// Users currently typing
private List<UserModel> TypingUsers = new()
{
MichaleUserModel
};
private List<ChatMessage> ChatUserMessages = new()
{
new ChatMessage()
{
Text = "Hi, thinking of painting this weekend.",
Author = CurrentUserModel
},
new ChatMessage()
{
Text = "That's fun! What will you paint?",
Author = MichaleUserModel
}
};
}Toggle Typing Indicator
Show/hide typing indicator on demand:
private async Task StartTyping(UserModel user)
{
if (!TypingUsers.Contains(user))
{
TypingUsers.Add(user);
StateHasChanged();
}
}
private async Task StopTyping(UserModel user)
{
TypingUsers.Remove(user);
StateHasChanged();
}
// Usage
private async Task UserStartsTyping()
{
await StartTyping(MichaleUserModel);
// Simulate typing duration
await Task.Delay(3000);
// Send message and stop typing
var message = new ChatMessage
{
Text = "Great! Let me know when you start.",
Author = MichaleUserModel
};
ChatUserMessages.Add(message);
await StopTyping(MichaleUserModel);
StateHasChanged();
}Multiple Users Typing
Show when multiple users are typing simultaneously:
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 600px;">
<SfChatUI
ID="groupChat"
User="CurrentUser"
TypingUsers="TypingUsers"
Messages="Messages">
</SfChatUI>
</div>
@code {
private UserModel CurrentUser = new UserModel()
{
ID = "user1",
User = "Albert"
};
private UserModel MichaleUser = new UserModel()
{
ID = "user2",
User = "Michale Suyama"
};
private UserModel ReenaUser = new UserModel()
{
ID = "user3",
User = "Reena"
};
private List<UserModel> TypingUsers = new();
private List<ChatMessage> Messages = new();
// Simulate multiple users typing
private async Task SimulateGroupTyping()
{
// Michale starts typing
TypingUsers.Add(MichaleUser);
StateHasChanged();
await Task.Delay(1000);
// Reena also starts typing
TypingUsers.Add(ReenaUser);
StateHasChanged();
await Task.Delay(2000);
// Michale sends message
Messages.Add(new ChatMessage
{
Text = "I have a suggestion",
Author = MichaleUser
});
TypingUsers.Remove(MichaleUser);
StateHasChanged();
await Task.Delay(1000);
// Reena sends message
Messages.Add(new ChatMessage
{
Text = "I was thinking the same thing",
Author = ReenaUser
});
TypingUsers.Remove(ReenaUser);
StateHasChanged();
}
}Dynamic Typing Status
Update Typing Status in Real-time
Manage typing status dynamically based on user input:
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 600px;">
<SfChatUI
ID="chat"
User="CurrentUser"
TypingUsers="TypingUsers"
Messages="Messages"
UserTyping="@OnUserTyping">
</SfChatUI>
</div>
@code {
private UserModel CurrentUser = new UserModel()
{
ID = "user1",
User = "You"
};
private List<UserModel> TypingUsers = new();
private List<ChatMessage> Messages = new();
private bool isUserTyping = false;
private async Task OnUserTyping(ChatUserTypingEventArgs args)
{
// User is typing
if (!isUserTyping)
{
isUserTyping = true;
// Notify other users (would send to server/SignalR)
await NotifyOthersUserTyping();
}
// Reset typing timer
await ResetTypingTimer();
}
private async Task ResetTypingTimer()
{
// After 3 seconds of no input, assume user stopped typing
await Task.Delay(3000);
isUserTyping = false;
// Notify others user stopped typing
await NotifyOthersUserStoppedTyping();
}
private async Task NotifyOthersUserTyping()
{
// Would communicate via SignalR or HTTP to other clients
// Others would add CurrentUser to their TypingUsers list
}
private async Task NotifyOthersUserStoppedTyping()
{
// Would communicate via SignalR or HTTP
// Others would remove CurrentUser from their TypingUsers list
}
}Typing User Management
Add/Remove Typing Users
Programmatically manage the typing users list:
private void AddTypingUser(UserModel user)
{
if (!TypingUsers.Any(u => u.ID == user.ID))
{
TypingUsers.Add(user);
StateHasChanged();
}
}
private void RemoveTypingUser(UserModel user)
{
TypingUsers.RemoveAll(u => u.ID == user.ID);
StateHasChanged();
}
private void ClearTypingUsers()
{
TypingUsers.Clear();
StateHasChanged();
}
private bool IsUserTyping(string userId)
{
return TypingUsers.Any(u => u.ID == userId);
}Typing Timeout Management
Automatically clear typing status after inactivity:
private Dictionary<string, System.Timers.Timer> TypingTimers = new();
private void StartTypingTimeout(UserModel user)
{
AddTypingUser(user);
// Cancel existing timer if any
if (TypingTimers.ContainsKey(user.ID))
{
TypingTimers[user.ID].Stop();
TypingTimers[user.ID].Dispose();
}
// Create new timeout timer (3 seconds)
var timer = new System.Timers.Timer(3000);
timer.Elapsed += (s, e) =>
{
RemoveTypingUser(user);
timer.Stop();
timer.Dispose();
TypingTimers.Remove(user.ID);
};
TypingTimers[user.ID] = timer;
timer.Start();
}Typing Indicator Templates
Customize the typing indicator appearance using templates:
Basic Typing Template
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 600px;">
<SfChatUI
ID="chat"
User="CurrentUser"
TypingUsers="TypingUsers"
Messages="Messages">
<TypingUsersTemplate>
<div class="typing-wrapper">
@for (int i = 0; i < context.Users.Count; i++)
{
if (i == context.Users.Count - 1 && i > 0)
{
<span>and </span>
}
<span class="typing-user">@context.Users[i].User</span>
}
<span> @(context.Users.Count == 1 ? "is" : "are") typing...</span>
</div>
</TypingUsersTemplate>
</SfChatUI>
</div>
<style>
.typing-wrapper {
display: flex;
gap: 4px;
align-items: center;
font-family: Arial, sans-serif;
font-size: 14px;
color: #555;
margin: 5px 0;
}
.typing-user {
font-weight: bold;
color: #0078d4;
}
</style>
@code {
private UserModel CurrentUser = new UserModel()
{
ID = "user1",
User = "Albert"
};
private UserModel MichaleUser = new UserModel()
{
ID = "user2",
User = "Michale"
};
private UserModel ReenaUser = new UserModel()
{
ID = "user3",
User = "Reena"
};
private List<UserModel> TypingUsers = new()
{
MichaleUser,
ReenaUser
};
private List<ChatMessage> Messages = new();
}Advanced Typing Template with Indicators
<SfChatUI ID="chat" User="CurrentUser" TypingUsers="TypingUsers" Messages="Messages">
<TypingUsersTemplate>
<div class="custom-typing">
<div class="typing-avatars">
@foreach (var user in context.Users)
{
<div class="typing-avatar" title="@user.User">
@user.User.Substring(0, 1)
</div>
}
</div>
<div class="typing-indicator">
<span></span><span></span><span></span>
</div>
</div>
</TypingUsersTemplate>
</SfChatUI>
<style>
.custom-typing {
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
}
.typing-avatars {
display: flex;
gap: -4px;
}
.typing-avatar {
width: 28px;
height: 28px;
border-radius: 50%;
background: #4a90e2;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
margin-left: -4px;
}
.typing-indicator {
display: flex;
gap: 3px;
}
.typing-indicator span {
width: 8px;
height: 8px;
border-radius: 50%;
background: #999;
animation: typing 1.4s infinite;
}
.typing-indicator span:nth-child(2) {
animation-delay: 0.2s;
}
.typing-indicator span:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes typing {
0%, 60%, 100% {
opacity: 0.5;
transform: translateY(0);
}
30% {
opacity: 1;
transform: translateY(-8px);
}
}
</style>Best Practices
1. Set Typing Timeout - Clear typing status after inactivity 2. Avoid Duplicate Users - Check if user already in TypingUsers before adding 3. Real-time Sync - Use SignalR or WebSockets to sync typing status 4. Performance - Limit number of typing indicators shown 5. User Feedback - Provide clear visual indication of who is typing 6. Stop on Send - Remove user from typing list when message is sent 7. Accessibility - Include text descriptions in typing templates