Vue Chat - Data Binding

You can bind data to Chat in two ways: with the items array or the dataSource entity.

NOTE
Do not specify the items property if you specified dataSource, and vice versa.

items

The items array contains all messages in the Chat. To display an initial message, add it to this array. Add subsequent messages to the array to render them in Chat.

jQuery
NOTE
The renderMessage(message) method is the primary way to render a new message in jQuery. Use items only to specify initial messages.
index.js
$(() => {
    $('#chat').dxChat({
        // ...
        items: [
            {
                timestamp: Date.now(),
                author: secondUser,
                text: "Hello! I'm here to help you. How can I assist you today?",
            }
        ];
        onMessageEntered: (e) => {
            e.component.renderMessage(e.message);
        },
    });
});
Angular
app.component.html
app.component.ts
<dx-chat 
    [items]="messages"
    (onMessageEntered)="onMessageEntered($event)"
></dx-chat>
import { DxChatTypes } from "devextreme-angular/ui/chat";
// ...
export class AppComponent {
    messages: DxChatTypes.Message[] = [
        {
            timestamp: Date.now(),
            author: secondUser,
            text: "Hello! I'm here to help you. How can I assist you today?",
        }
    ];
    onMessageEntered({ message }) {
        this.messages = [...this.messages, message];
    }
}
Vue
App.vue
<template>
<DxChat
    :items="messages"
    @message-entered="onMessageEntered"
/>
</template>

<script setup>
import DxChat from "devextreme-vue/chat";
const messages = [
    {
        timestamp: Date.now(),
        author: secondUser,
        text: "Hello! I'm here to help you. How can I assist you today?",
    }
];
const onMessageEntered = ({ message }) => {
    messages.value = [...messages.value, message];
};
</script>
React
App.js
import React, { useCallback, useState } from "react";
import Chat from "devextreme-react/chat";

const initialMessage = {
    timestamp: Date.now(),
    author: secondUser,
    text: "Hello! I'm here to help you. How can I assist you today?",
};

const App = () => {
    const [messages, setMessages] = useState(initialMessage);
    const onMessageEntered = useCallback(({ message }) => {
        setMessages((prevMessages) => [...prevMessages, message]);
    }, []);

    return (
        <Chat
            onMessageEntered={onMessageEntered}
            items={messages}
        />
    );
};

dataSource

For a more flexible solution than one achieved with the use of an array, specify the dataSource property. For instance, with dataSource, Chat can handle server-side data processing through Web API, a store, or a DataSource object.

When you pass a store to dataSource, the DataSource instance is created automatically inside the Chat.

NOTE
If you use a separate DataSource object, disable pagination to ensure Chat functions properly.

When you use dataSource, the reloadOnChange property is crucial:

  • When you send a message in a Chat (press the "Send" button), the Chat triggers the store's insert method and adds the message to the store.
  • If reloadOnChange is enabled (default), the dataSource reloads: clears all items and calls the load method to update itself. Chat automatically listens to dataSource changes, and updates the message feed with new messages.
  • Disable reloadOnChange to manage large numbers of messages, prevent additional load requests, and control message rendering timing.

The following code snippet includes:

  • A CustomStore.
  • A DataSource (pagination disabled).
  • Push API for inserting and updating messages.
  • reloadOnChange: false.
NOTE
This code snippet illustrates basic principles and does not implement custom editing. For a complete application example, refer to the following demo: AI and Chatbot Integration.
jQuery
index.js
const messages = [
    {
        timestamp: new Date(),
        text: "Hello! I'm here to help you. How can I assist you today?"
    }
];

let uniqueIndex = messages.length + 1;
let editing = null;

$(() => {
    const customStore = new DevExpress.data.CustomStore({
        key: "id",
        load: () => {
            const d = $.Deferred();

            setTimeout(() => {
                d.resolve([...messages]);
            });

            return d.promise();
        },
        insert: (message) => {
            const d = $.Deferred();

            setTimeout(() => {
                messages.push(message);
                d.resolve();
            });

            return d.promise();
        },
    });

    const dataSource = new DevExpress.data.DataSource({
        store: customStore,
        paginate: false
    });

    $("#chat").dxChat({
        dataSource,
        reloadOnChange: false,
        onMessageEntered: (e) => {
            if (editing) {
                dataSource.store().push([{ type: "update", key: editing, data: { text: e.message.text } }]);
                editing = null;
            }
            else {
                dataSource.store().push([
                    { type: "insert", data: { id: uniqueIndex++, ...e.message } }
                ]);
            }
        },
    });
});
Angular
app.component.html
app.component.ts
<dx-chat
    [dataSource]="dataSource"
    [reloadOnChange]="false"
    (onMessageEntered)="onMessageEntered($event)">
</dx-chat>
import DataSource from 'devextreme/data/data_source';
import CustomStore from 'devextreme/data/custom_store';
import { DxChatTypes } from "devextreme-angular/ui/chat"; 
// ...

export class AppComponent {
    messages: DxChatTypes.Message[] = [{
        timestamp: new Date(),
        text: "Hello! I'm here to help you. How can I assist you today?"
    }];

    customStore: CustomStore = new CustomStore({
        key: "id",
        load: () => {
            return new Promise((resolve) => {
                setTimeout(() => {
                    resolve([...this.messages]);
                }, 0);
            });
        },
        insert: (message) => {
            return new Promise((resolve) => {
                setTimeout(() => {
                    this.messages.push(message);
                    resolve(message);
                });
            });
        },
    });

    dataSource: DataSource = new DataSource({
        store: this.customStore,
        paginate: false,
    });

    editing = null;

    uniqueIndex = this.messages.length + 1;

    onMessageEntered(e: DxChatTypes.MessageEnteredEvent) {
        if (this.editing) {
            this.dataSource.store().push([{ type: "update", key: this.editing, data: { text: e.message.text } }]);
            this.editing = null;
        }
        else {
            this.dataSource.store().push([
                { type: "insert", data: { id: this.uniqueIndex++, ...e.message } }
            ]);
        }
    };
}
Vue
App.vue
<template>
<DxChat 
    :data-source="dataSource"
    :reload-on-change="false"
    @message-entered="onMessageEntered"
/>
</template>

<script setup lang="ts">
import { DxChat } from 'devextreme-vue';
import type { DxChatTypes } from 'devextreme-vue/chat';
import DataSource from 'devextreme/data/data_source';
import CustomStore from 'devextreme/data/custom_store';

const messages: DxChatTypes.Message[] = [{
    timestamp: new Date(),
    text: "Hello! I'm here to help you. How can I assist you today?"
}];
const customStore = new CustomStore({
    key: "id",
    load: () => {
        return new Promise((resolve) => {
            setTimeout(() => {
                resolve([...messages]);
            }, 0);
        });
    },
    insert: (message) => {
        return new Promise((resolve) => {
            setTimeout(() => {
                messages.push(message);
                resolve(message);
            });
        });
    },
});

const dataSource = new DataSource({
    store: customStore,
    paginate: false,
});

let editing = null;

let uniqueIndex = messages.length + 1;

const onMessageEntered = (e: DxChatTypes.MessageEnteredEvent) => {
    if (editing) {
        dataSource.store().push([{ type: "update", key: editing, data: { text: e.message.text } }]);
        editing = null;
    }
    else {
        dataSource.store().push([
            { type: "insert", data: { id: uniqueIndex++, ...e.message } }
        ]);
    }
};
</script>
React
App.tsx
import { Chat } from "devextreme-react";
import { ChatTypes } from "devextreme-react/chat";
import "devextreme/dist/css/dx.light.css";
import { useCallback } from "react";
import DataSource from "devextreme/data/data_source";
import CustomStore from "devextreme/data/custom_store";

export default function App() {
    const messages: ChatTypes.Message[] = [
        {
            timestamp: new Date(),
            text: "Hello! I'm here to help you. How can I assist you today?",
        },
    ];
    const customStore = new CustomStore({
        key: "id",
        load: (): Promise<ChatTypes.Message[]> =>
        new Promise((resolve) => {
            setTimeout(() => {
                resolve([...messages]);
            }, 0);
        }),
        insert: (message: ChatTypes.Message): Promise<ChatTypes.Message> =>
        new Promise((resolve) => {
            setTimeout(() => {
                messages.push(message);
                resolve(message);
            });
        }),
    });

    const dataSource = new DataSource({
        store: customStore,
        paginate: false,
    });

    let editing = null;

    let uniqueIndex = messages.length + 1;

    const onMessageEntered = useCallback((e: ChatTypes.MessageEnteredEvent) => {
        if (editing) {
            dataSource.store().push([
                { type: "update", key: editing, data: { text: e.message!.text } },
            ]);
            editing = null;
        } else {
        dataSource.store().push([{ type: "insert", data: { id: uniqueIndex++, ...e.message } }]);
        }
    }, []);
    return (
        <Chat
            dataSource={dataSource}
            reloadOnChange={false}
            onMessageEntered={onMessageEntered}
        />
    );
}