A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://vaadin.com/docs/latest/components/message-list below:

Message List component | Vaadin components

  1. Docs
  2. Components
  3. Message List

Message List allows you to show a list of messages, for example, a chat log. You can configure the text content, information about the sender, and the time of sending for each message.

Open in a
new tab
Source code MessageListComponent.java

package com.vaadin.demo.component.messages; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Arrays; import com.vaadin.demo.domain.DataService; import com.vaadin.demo.domain.Person; import com.vaadin.flow.component.html.Div; import com.vaadin.flow.component.messages.MessageList; import com.vaadin.flow.component.messages.MessageListItem; import com.vaadin.flow.router.Route; @Route("message-list") public class MessageListComponent extends Div { public MessageListComponent() { // tag::snippet[] Person person = DataService.getPeople(1).get(0); MessageList list = new MessageList(); Instant yesterday = LocalDateTime.now().minusDays(1) .toInstant(ZoneOffset.UTC); Instant fiftyMinsAgo = LocalDateTime.now().minusMinutes(50) .toInstant(ZoneOffset.UTC); MessageListItem message1 = new MessageListItem( "Linsey, could you check if the details with the order are okay?", yesterday, "Matt Mambo"); message1.setUserColorIndex(1); MessageListItem message2 = new MessageListItem("All good. Ship it.", fiftyMinsAgo, "Linsey Listy", person.getPictureUrl()); message2.setUserColorIndex(2); list.setItems(Arrays.asList(message1, message2)); add(list); // end::snippet[] } }

message-list-component.tsx

import React, { useEffect } from 'react'; import { format, subDays, subMinutes } from 'date-fns'; import { useSignal } from '@vaadin/hilla-react-signals'; import { MessageList } from '@vaadin/react-components/MessageList.js'; import { getPeople } from 'Frontend/demo/domain/DataService'; import type Person from 'Frontend/generated/com/vaadin/demo/domain/Person'; function Example() { // tag::snippet[] const person = useSignal<Person | undefined>(undefined); useEffect(() => { getPeople({ count: 1 }).then(({ people }) => { person.value = people[0]; }); }, []); const isoMinutes = 'yyyy-MM-dd HH:mm'; const yesterday = format(subDays(new Date(), 1), isoMinutes); const fiftyMinutesAgo = format(subMinutes(new Date(), 50), isoMinutes); const items = [ { text: 'Linsey, could you check if the details with the order are okay?', time: yesterday, userName: 'Matt Mambo', userColorIndex: 1, }, { text: 'All good. Ship it.', time: fiftyMinutesAgo, userName: 'Linsey Listy', userColorIndex: 2, userImg: person.value?.pictureUrl, }, ]; return <MessageList items={items} />; // end::snippet[] }

message-list-component.ts

import '@vaadin/message-list'; import { format, subDays, subMinutes } from 'date-fns'; import { html, LitElement } from 'lit'; import { customElement } from 'lit/decorators.js'; import { getPeople } from 'Frontend/demo/domain/DataService'; import type Person from 'Frontend/generated/com/vaadin/demo/domain/Person'; import { applyTheme } from 'Frontend/generated/theme'; @customElement('message-list-component') export class Example extends LitElement { private person: Person | undefined; private isoMinutes = 'yyyy-MM-dd HH:mm'; private yesterday = format(subDays(new Date(), 1), this.isoMinutes); private fiftyMinutesAgo = format(subMinutes(new Date(), 50), this.isoMinutes); protected override createRenderRoot() { const root = super.createRenderRoot(); // Apply custom theme (only supported if your app uses one) applyTheme(root); return root; } protected override async firstUpdated() { const { people } = await getPeople({ count: 1 }); this.person = people[0]; this.requestUpdate(); } protected override render() { return html` <!-- tag::snippet[] --> <vaadin-message-list .items="${[ { text: 'Linsey, could you check if the details with the order are okay?', time: this.yesterday, userName: 'Matt Mambo', userColorIndex: 1, }, { text: 'All good. Ship it.', time: this.fiftyMinutesAgo, userName: 'Linsey Listy', userColorIndex: 2, userImg: this.person ? this.person.pictureUrl : undefined, }, ]}" ></vaadin-message-list> <!-- end::snippet[] --> `; } }

The messages in the list can be populated with the items property. The items property is of type Array, with JSON objects in it. Each JSON object is a single message.

Styling

You can style individual messages by adding a theme property to some items and providing CSS for that theme. The following example shows how to highlight the current user’s own messages:

Open in a
new tab
Source code MessageListWithThemeComponent.java

package com.vaadin.demo.component.messages; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Arrays; import com.vaadin.demo.domain.DataService; import com.vaadin.demo.domain.Person; import com.vaadin.flow.component.html.Div; import com.vaadin.flow.component.messages.MessageList; import com.vaadin.flow.component.messages.MessageListItem; import com.vaadin.flow.router.Route; @Route("message-list-with-theme") public class MessageListWithThemeComponent extends Div { public MessageListWithThemeComponent() { // tag::snippet[] Person person = DataService.getPeople(1).get(0); MessageList list = new MessageList(); Instant yesterday = LocalDateTime.now(ZoneOffset.UTC).minusDays(1) .toInstant(ZoneOffset.UTC); MessageListItem message1 = new MessageListItem( "Linsey, could you check if the details with the order are okay?", yesterday, "Matt Mambo"); message1.setUserColorIndex(1); Instant fiftyMinsAgo = LocalDateTime.now(ZoneOffset.UTC) .minusMinutes(50).toInstant(ZoneOffset.UTC); MessageListItem message2 = new MessageListItem("All good. Ship it.", fiftyMinsAgo, "Linsey Listy", person.getPictureUrl()); message2.setUserColorIndex(2); // Add custom class name message2.addClassNames("current-user"); list.setItems(Arrays.asList(message1, message2)); add(list); // end::snippet[] } }

message-list-theming.css

/* Add this to your global CSS, for example in: */ /* frontend/theme/[my-theme]/styles.css */ vaadin-message.current-user { background-color: #000; color: #fff; border: 2px solid #fff; border-radius: 9px; font-weight: 900; } vaadin-message.current-user::part(name) { font-weight: 900; } vaadin-message.current-user::part(time) { color: #fff; } vaadin-message.current-user::part(name)::after{ content: " (You)"; }

message-list-theming.css message-list-with-theme-component.tsx

import React, { useEffect } from 'react'; import { format, subDays, subMinutes } from 'date-fns'; import { useSignal } from '@vaadin/hilla-react-signals'; import { MessageList } from '@vaadin/react-components/MessageList.js'; import { getPeople } from 'Frontend/demo/domain/DataService'; import type Person from 'Frontend/generated/com/vaadin/demo/domain/Person'; function Example() { const person = useSignal<Person | undefined>(undefined); useEffect(() => { getPeople({ count: 1 }).then(({ people }) => { person.value = people[0]; }); }, []); const isoMinutes = 'yyyy-MM-dd HH:mm'; const yesterday = format(subDays(new Date(), 1), isoMinutes); const fiftyMinutesAgo = format(subMinutes(new Date(), 50), isoMinutes); return ( // tag::snippet[] <MessageList items={[ { text: 'Linsey, could you check if the details with the order are okay?', time: yesterday, userName: 'Matt Mambo', userColorIndex: 1, }, { text: 'All good. Ship it.', time: fiftyMinutesAgo, userName: 'Linsey Listy', userColorIndex: 2, userImg: person.value?.pictureUrl, className: 'current-user', }, ]} /> // end::snippet[] ); }

message-list-theming.css

/* Add this to your global CSS, for example in: */ /* frontend/theme/[my-theme]/styles.css */ vaadin-message.current-user { background-color: #000; color: #fff; border: 2px solid #fff; border-radius: 9px; font-weight: 900; } vaadin-message.current-user::part(name) { font-weight: 900; } vaadin-message.current-user::part(time) { color: #fff; } vaadin-message.current-user::part(name)::after{ content: " (You)"; }

message-list-theming.css message-list-with-theme-component.ts

import '@vaadin/message-list'; import { format, subDays, subMinutes } from 'date-fns'; import { html, LitElement } from 'lit'; import { customElement } from 'lit/decorators.js'; import { getPeople } from 'Frontend/demo/domain/DataService'; import type Person from 'Frontend/generated/com/vaadin/demo/domain/Person'; import { applyTheme } from 'Frontend/generated/theme'; @customElement('message-list-component-with-theme') export class Example extends LitElement { private person: Person | undefined; private isoMinutes = 'yyyy-MM-dd HH:mm'; private yesterday = format(subDays(new Date(), 1), this.isoMinutes); private fiftyMinutesAgo = format(subMinutes(new Date(), 50), this.isoMinutes); protected override createRenderRoot() { const root = super.createRenderRoot(); // Apply custom theme (only supported if your app uses one) applyTheme(root); return root; } protected override async firstUpdated() { const { people } = await getPeople({ count: 1 }); this.person = people[0]; this.requestUpdate(); } protected override render() { return html` <!-- tag::snippet[] --> <vaadin-message-list .items="${[ { text: 'Linsey, could you check if the details with the order are okay?', time: this.yesterday, userName: 'Matt Mambo', userColorIndex: 1, }, { text: 'All good. Ship it.', time: this.fiftyMinutesAgo, userName: 'Linsey Listy', userColorIndex: 2, userImg: this.person ? this.person.pictureUrl : undefined, /* Add custom class name */ className: 'current-user', }, ]}" ></vaadin-message-list> <!-- end::snippet[] --> `; } }

message-list-theming.css

/* Add this to your global CSS, for example in: */ /* frontend/theme/[my-theme]/styles.css */ vaadin-message.current-user { background-color: #000; color: #fff; border: 2px solid #fff; border-radius: 9px; font-weight: 900; } vaadin-message.current-user::part(name) { font-weight: 900; } vaadin-message.current-user::part(time) { color: #fff; } vaadin-message.current-user::part(name)::after{ content: " (You)"; }

message-list-theming.css

Note

Use Theme Names, Not Class Names pre-V24.3

In versions prior to 24.3, theme names must be used instead of class names (theme property / addThemeNames Java method). The CSS syntax for targeting a theme name is [theme~="custom-theme"]. Markdown

Use Markdown formatting in Message List to display rich text instead of plain text.

Markdown syntax gives you rich text formatting with headings, lists, links, and more. You can also use HTML tags within Markdown content. Message List automatically sanitizes content to prevent rendering dangerous HTML.

Open in a
new tab
Source code MessageListMarkdown.java

package com.vaadin.demo.component.messages; import com.vaadin.flow.component.html.Div; import com.vaadin.flow.component.messages.MessageList; import com.vaadin.flow.component.messages.MessageListItem; import com.vaadin.flow.router.Route; @Route("message-list-markdown") public class MessageListMarkdown extends Div { public MessageListMarkdown() { // tag::snippet[] MessageList list = new MessageList(); list.setItems(new MessageListItem( "**Hello team!** Did everyone review the *design document* for the new project?", "Alex Johnson"), new MessageListItem( """ ## Project Update I've completed the initial research phase and documented my findings. * UI mockups ✅ * Market analysis ✅ * [See detailed report](https://vaadin.com) Let me know your thoughts! """, "Sam Rivera")); list.setMarkdown(true); // end::snippet[] add(list); } }

message-list-markdown.tsx

import { useEffect } from 'react'; import { useSignal } from '@vaadin/hilla-react-signals'; import { MessageList, type MessageListItem } from '@vaadin/react-components/MessageList.js'; function Example() { // tag::snippet[] const items = useSignal<MessageListItem[]>([]); useEffect(() => { items.value = [ { text: '**Hello team!** Did everyone review the *design document* for the new project?', userName: 'Alex Johnson', }, { text: `## Project Update I've completed the initial research phase and documented my findings. * UI mockups ✅ * Market analysis ✅ * [See detailed report](https://vaadin.com) Let me know your thoughts!`, userName: 'Sam Rivera', }, ]; }, []); return <MessageList items={items.value} markdown />; // end::snippet[] }

message-list-markdown.ts

import '@vaadin/message-list'; import { html, LitElement } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import type { MessageListItem } from '@vaadin/message-list'; import { applyTheme } from 'Frontend/generated/theme'; @customElement('message-list-markdown') export class Example extends LitElement { protected override createRenderRoot() { const root = super.createRenderRoot(); // Apply custom theme (only supported if your app uses one) applyTheme(root); return root; } // tag::snippet[] @state() private items: MessageListItem[] = [ { text: '**Hello team!** Did everyone review the *design document* for the new project?', userName: 'Alex Johnson', }, { text: `## Project Update I've completed the initial research phase and documented my findings. * UI mockups ✅ * Market analysis ✅ * [See detailed report](https://vaadin.com) Let me know your thoughts!`, userName: 'Sam Rivera', }, ]; protected override render() { return html`<vaadin-message-list .items="${this.items}" markdown></vaadin-message-list>`; } // end::snippet[] }

Usage for AI Chats

Combine Message List with Message Input to create effective AI chat interfaces. Build your AI chat interface with:

Follow these best practices for a smooth user experience:

Open in a
new tab
Source code MessageListAiChat.java

package com.vaadin.demo.component.messages; import java.util.List; import com.vaadin.demo.component.messages.LLMClient.Message; import com.vaadin.flow.component.html.Div; import com.vaadin.flow.component.messages.MessageInput; import com.vaadin.flow.component.messages.MessageList; import com.vaadin.flow.component.messages.MessageListItem; import com.vaadin.flow.router.Route; import com.vaadin.flow.theme.lumo.LumoUtility; @Route("message-list-ai-chat") public class MessageListAiChat extends Div { private MessageListItem createItem(String text, boolean assistant) { MessageListItem item = new MessageListItem(text, assistant ? "Assistant" : "User"); item.setUserColorIndex(assistant ? 2 : 1); return item; } public MessageListAiChat() { String chatId = "1234"; // Placeholder chat identifier // tag::snippet[] MessageList list = new MessageList(); list.setMarkdown(true); // end::snippet[] MessageInput input = new MessageInput(); // Live region for screen reader announcements Div liveRegion = new Div(); liveRegion.getElement().setAttribute("aria-live", "polite"); liveRegion.addClassName(LumoUtility.Accessibility.SCREEN_READER_ONLY); add(liveRegion); List<Message> history = LLMClient.getHistory(chatId); list.setItems(history.stream() .map(message -> createItem(message.text(), message.assistant())) .toList()); input.addSubmitListener(e -> { String userInput = e.getValue(); // Add the user message to the list list.addItem(createItem(userInput, false)); // Add the Assistant message to the list MessageListItem newAssistantMessage = createItem("", true); list.addItem(newAssistantMessage); // Announce that AI is processing liveRegion.setText("AI is processing the prompt"); int messageListItemCount = list.getItems().size(); LLMClient.stream(chatId, userInput).subscribe(token -> { getUI().get().access(() -> { // Update the Assistant message with the response // Make sure to have server push enabled! // See // https://vaadin.com/docs/latest/flow/advanced/server-push newAssistantMessage.appendText(token); }); }, error -> { // Handle error }, () -> { getUI().get().access(() -> { if (messageListItemCount != list.getItems().size()) { // Another message is still being processed return; } // Announce that a new message is available liveRegion.setText("New message available"); }); }); }); add(list, input); } }

message-list-ai-chat.tsx

import { useCallback, useEffect } from 'react'; import { useSignal } from '@vaadin/hilla-react-signals'; import { MessageInput, type MessageInputSubmitEvent } from '@vaadin/react-components'; import { MessageList, type MessageListItem } from '@vaadin/react-components/MessageList.js'; import LLMChatService from 'Frontend/demo/services/LLMChatService.js'; function createItem(text: string, assistant = false): MessageListItem { return { text, userName: assistant ? 'Assistant' : 'User', userColorIndex: assistant ? 2 : 1, }; } function Example() { const messageListItems = useSignal<MessageListItem[]>([]); const announcement = useSignal(''); const chatId = '1234'; // Placeholder chat identifier useEffect(() => { LLMChatService.getHistory(chatId).then((messages) => { messageListItems.value = messages.map((message) => createItem(message.text, message.assistant) ); }); }, []); const handleChatSubmit = useCallback((e: MessageInputSubmitEvent) => { const userInput = e.detail.value; // Add the user message to the list messageListItems.value = [...messageListItems.value, createItem(userInput)]; // Add the Assistant message to the list const newAssistantItem = createItem('', true); messageListItems.value = [...messageListItems.value, newAssistantItem]; // Announce that AI is processing announcement.value = 'AI is processing the prompt'; LLMChatService.stream(chatId, userInput) .onNext((token) => { // Append the token to the Assistant message newAssistantItem.text += token; // Force the MessageList to re-render messageListItems.value = [...messageListItems.value]; }) .onComplete(() => { // Announce that a new message is available announcement.value = 'New message available'; }); }, []); return ( <div> {/* Live region for screen reader announcements */} <div aria-live="polite" className="sr-only"> {announcement.value} </div> {/* tag::snippet[] */} <MessageList items={messageListItems.value} markdown /> {/* end::snippet[] */} <MessageInput onSubmit={handleChatSubmit} /> </div> ); }

LLMChatService.java

package com.vaadin.demo.component.messages; import java.util.List; import com.vaadin.demo.component.messages.LLMClient.Message; import com.vaadin.flow.server.auth.AnonymousAllowed; import com.vaadin.hilla.BrowserCallable; import reactor.core.publisher.Flux; // tag::snippet[] @BrowserCallable @AnonymousAllowed public class LLMChatService { public List<Message> getHistory(String chatId) { // Implement your message history retrieval logic here return LLMClient.getHistory(chatId); } public Flux<String> stream(String chatId, String userMessage) { // Implement your message streaming logic here return LLMClient.stream(chatId, userMessage); } } // end::snippet[]

message-list-ai-chat.ts

import '@vaadin/message-list'; import '@vaadin/message-input'; import { html, LitElement } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import type { MessageInputSubmitEvent } from '@vaadin/message-input'; import type { MessageListItem } from '@vaadin/message-list'; import LLMChatService from 'Frontend/demo/services/LLMChatService.js'; import { applyTheme } from 'Frontend/generated/theme'; function createItem(text: string, assistant = false): MessageListItem { return { text, userName: assistant ? 'Assistant' : 'User', userColorIndex: assistant ? 2 : 1, }; } @customElement('message-list-ai-chat') export class Example extends LitElement { protected override createRenderRoot() { const root = super.createRenderRoot(); // Apply custom theme (only supported if your app uses one) applyTheme(root); return root; } @state() private messageListItems: MessageListItem[] = []; @state() private announcement: string = ''; private chatId = '1234'; // Placeholder chat identifier protected override async firstUpdated() { const messages = await LLMChatService.getHistory(this.chatId); this.messageListItems = messages.map((message) => createItem(message.text, message.assistant)); } protected override render() { return html` <!-- Live region for screen reader announcements --> <div aria-live="polite" class="sr-only">${this.announcement}</div> <!-- tag::snippet[] --> <vaadin-message-list .items="${this.messageListItems}" markdown></vaadin-message-list> <!-- end::snippet[] --> <vaadin-message-input @submit="${this.handleChatSubmit}"></vaadin-message-input> `; } private handleChatSubmit(e: MessageInputSubmitEvent) { const userInput = e.detail.value; // Add the user message to the list this.messageListItems = [...this.messageListItems, createItem(userInput)]; // Add the Assistant message to the list const newAssistantItem = createItem('', true); this.messageListItems = [...this.messageListItems, newAssistantItem]; // Announce that AI is processing this.announcement = 'AI is processing the prompt'; LLMChatService.stream(this.chatId, userInput) .onNext((token) => { // Append the token to the Assistant message newAssistantItem.text += token; // Force the MessageList to re-render this.messageListItems = [...this.messageListItems]; }) .onComplete(() => { // Announce that a new message is available this.announcement = 'New message available'; }); } }

LLMChatService.java

package com.vaadin.demo.component.messages; import java.util.List; import com.vaadin.demo.component.messages.LLMClient.Message; import com.vaadin.flow.server.auth.AnonymousAllowed; import com.vaadin.hilla.BrowserCallable; import reactor.core.publisher.Flux; // tag::snippet[] @BrowserCallable @AnonymousAllowed public class LLMChatService { public List<Message> getHistory(String chatId) { // Implement your message history retrieval logic here return LLMClient.getHistory(chatId); } public Flux<String> stream(String chatId, String userMessage) { // Implement your message streaming logic here return LLMClient.stream(chatId, userMessage); } } // end::snippet[]

Component Usage recommendations

Message Input

Allow users to author and send messages.

Markdown

Standalone component for rendering Markdown content.

EE10DB8E-479C-41D0-9A77-BFA41C340BFB

Message InputStylingMessage ListStyling

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4