The DevExtreme Chat allows users to attach files. When this feature is activated (fileUploaderOptions.uploadFile is specified), an “Attach” button appears in the message input field, allowing users to add files to their messages.
When users attach files, each file is displayed in the input area with a file-type icon, basic details (name and size), upload status, and an option to remove files before sending.
You can customize the file upload process with the following fileUploaderOptions properties:
false, limits uploads to a single file.For the complete list of configuration options, refer to the following API section: fileUploaderOptions.
Attachment type includes name and size fields. To add custom fields (such as url in this demo), handle the onMessageEntered event and update the message object’s attachments array as needed. You can use this handler to save files to your server.
After a user sends a message, attachments appear in the corresponding message bubble. To allow users to download attachments, implement the onAttachmentDownloadClick event handler. You can define custom download logic within the handler.
@model DevExtreme.MVC.Demos.ViewModels.ChatViewModel
<div class="chat-container">
@(Html.DevExtreme().Chat()
.ID("chat")
.Height(710)
.User(user => user
.Id(Model.CurrentUser.Id)
.Name(Model.CurrentUser.Name)
)
.DataSource(new JS("dataSource"))
.ReloadOnChange(false)
.FileUploaderOptions(options => options
.OnUploaded("chat_fileUploaderOptions_onUploaded")
.UploadFile("chat_fileUploaderOptions_uploadFile")
)
.OnMessageEntered("chat_onMessageEntered")
.OnAttachmentDownloadClick("chat_onAttachmentDownloadClick")
)
</div>
<script>
const user = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Model.CurrentUser));
const store = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Model.Messages));
const customStore = new DevExpress.data.CustomStore({
key: "id",
load: async () => [...store],
insert: async (message) => {
store.push(message);
return message;
},
});
const dataSource = new DevExpress.data.DataSource({
store: customStore,
paginate: false,
});
const uploadedFilesMap = new Map();
function getFileUrl(filename) {
return uploadedFilesMap.get(filename);
}
function chat_fileUploaderOptions_onUploaded({ file }) {
const url = URL.createObjectURL(file);
uploadedFilesMap.set(file.name, url);
}
function chat_fileUploaderOptions_uploadFile() { }
function chat_onMessageEntered({ message }) {
const attachmentsWithUrls = message.attachments?.map((attachment) => ({
...attachment,
url: getFileUrl(attachment.name),
}));
dataSource.store().push([{
type: 'insert',
data: {
id: new DevExpress.data.Guid(),
...message,
attachments: attachmentsWithUrls,
},
}]);
}
function chat_onAttachmentDownloadClick({ attachment }) {
if (!attachment?.url) {
return;
}
const link = document.createElement('a');
link.setAttribute('href', attachment.url);
link.setAttribute('download', attachment.name);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
</script>
using System;
using System.Linq;
using System.Collections.Generic;
using System.Web.Mvc;
using DevExtreme.MVC.Demos.Models.Chat;
using DevExtreme.MVC.Demos.Models.SampleData;
using DevExtreme.MVC.Demos.ViewModels;
namespace DevExtreme.MVC.Demos.Controllers {
public class ChatController : Controller {
public ActionResult FileAttachments() {
return View(new ChatViewModel {
CurrentUser = SampleData.CurrentUser,
SupportAgent = SampleData.SupportAgent,
Messages = SampleData.MessagesWithAttachments,
});
}
}
}
using DevExtreme.MVC.Demos.Models.Chat;
using System;
using System.Collections.Generic;
namespace DevExtreme.MVC.Demos.Models.SampleData {
public partial class SampleData {
private static readonly DateTime todayDate = DateTime.Now.Date;
private static DateTime GetTimestamp(DateTime date, int offsetMinutes = 0) {
DateTime adjustedDate = date.AddMinutes(offsetMinutes);
return adjustedDate;
}
public static ChatUser CurrentUser = new ChatUser {
Id = "c94c0e76-fb49-4b9b-8f07-9f93ed93b4f3",
Name = "John Doe"
};
public static ChatUser SupportAgent = new ChatUser {
Id = "d16d1a4c-5c67-4e20-b70e-2991c22747c3",
Name = "Support Agent",
AvatarUrl = "../../Content/Images/petersmith.png"
};
public static readonly IEnumerable<Message> Messages = new[] {
new Message {
Timestamp = GetTimestamp(todayDate, -9),
Author = SupportAgent,
Text = "Hello, John!\nHow can I assist you today?",
},
new Message {
Timestamp = GetTimestamp(todayDate, -7),
Author = CurrentUser,
Text = "Hi, I'm having trouble accessing my account.",
},
new Message {
Timestamp = GetTimestamp(todayDate, -7),
Author = CurrentUser,
Text = "It says my password is incorrect."
},
new Message {
Timestamp = GetTimestamp(todayDate, -7),
Author = SupportAgent,
Text = "I can help you with that. Can you please confirm your UserID for security purposes?"
},
new Message {
Timestamp = GetTimestamp(todayDate, 1),
Author = CurrentUser,
Text = "john.doe1357"
},
new Message {
Timestamp = GetTimestamp(todayDate, 1),
Author = SupportAgent,
Text = "✅ Instructions to restore access have been sent to the email address associated with your account."
}
};
public static readonly IEnumerable<Message> MessagesWithAttachments = new[] {
new Message {
Timestamp = GetTimestamp(todayDate, -7),
Author = CurrentUser,
Text = "Hi! I'm having trouble accessing my account.\nThe website says my password is incorrect. I'm sending a few screenshots so you can see where I get the error.",
Attachments = new[] {
new MessageAttachment {
Name = "Pic1.png",
Size = 5842,
Url = "../../Content/images/Chat/Pic1.png",
},
new MessageAttachment {
Name = "Pic2.png",
Size = 6083,
Url = "../../Content/images/Chat/Pic2.png",
},
new MessageAttachment {
Name = "Pic3.png",
Size = 6180,
Url = "../../Content/images/Chat/Pic3.png",
}
},
},
new Message {
Timestamp = GetTimestamp(todayDate, -7),
Author = SupportAgent,
Text = "Hello! Thanks for including screenshots. To restore access, please follow instructions in the attached file.\nLet me know if you need anything else.",
Attachments = new[] {
new MessageAttachment {
Name = "Instructions.png",
Size = 5499,
Url = "../../Content/Images/Chat/Instructions.png",
}
},
}
};
}
}
using DevExtreme.MVC.Demos.Models.Chat;
using System.Collections.Generic;
namespace DevExtreme.MVC.Demos.ViewModels {
public class ChatViewModel {
public IEnumerable<Message> Messages { get; set; }
public IEnumerable<Message> MessagesWithAttachments { get; set; }
public ChatUser CurrentUser { get; set; }
public ChatUser SupportAgent { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace DevExtreme.MVC.Demos.Models.Chat
{
public class Message {
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("timestamp")]
public DateTime Timestamp { get; set; }
[JsonPropertyName("author")]
public ChatUser Author { get; set; }
[JsonPropertyName("text")]
public string Text { get; set; }
[JsonPropertyName("isDeleted")]
public Boolean IsDeleted { get; set; }
[JsonPropertyName("isEdited")]
public Boolean IsEdited { get; set; }
[JsonPropertyName("attachments")]
public IEnumerable<MessageAttachment> Attachments { get; set; }
}
}
using System.Text.Json.Serialization;
namespace DevExtreme.MVC.Demos.Models.Chat
{
public class ChatUser {
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("avatarUrl")]
public string AvatarUrl { get; set; }
}
}
.demo-container {
min-width: 720px;
}
.chat-container {
display: flex;
flex-grow: 1;
align-items: center;
justify-content: center;
}
.dx-chat {
max-width: 480px;
}
.caption {
font-size: var(--dx-font-size-sm);
font-weight: 500;
}
.dx-avatar {
border: 1px solid var(--dx-color-border);
}