NewPower Apps · canvas componentData display✓ Tested in StudioMIT License
Chat thread
Messages from a table as bubbles, yours on the right in your accent colour and theirs on the left with initials, each with its time and sent, delivered, read or not sent with Retry; a typing indicator with bouncing dots; a composer with OnSend; light and dark, a new bubble popping in.
- Name in Studio
- lcsChatThread
- Version
- 0.1.0
- Properties
- 13 inputs · 3 outputs · 2 events · 3 actions
Variations
As it is when you paste it, with every input at its default.
Hi, the projector in room 4 won't connect. Can someone take a look before the 2 o'clock?
6:12 AM
On it. Is it the HDMI or the wireless?
6:14 AM · Read
Wireless. The HDMI works but the cable is too short for the lectern.
6:16 AM
I'll bring a longer cable and reset the receiver. Ten minutes.
6:52 AM · Delivered
Draft: "" · Count: 4 · LastKey: m4
Formulas on this screen
Set the same ones in Studio to try it the same way; there, each Notify shows its event in a banner, which this preview leaves out so nothing covers the component. The instance names are the ones Studio gives the first copy on a screen.
lcsChatThread_1.OnSend = Collect(colChat, {Key: Text(GUID()), Text: Text, From: "me", When: Now(), Status: "sent", Initials: ""}); Notify("Sent: " & Text)
lcsChatThread_1.OnRetry = Notify("Retry " & Key)
Text1.Text = "Draft: """ & lcsChatThread_1.Draft & """ · Count: " & lcsChatThread_1.Count & " · LastKey: " & lcsChatThread_1.LastKey
A web replica that behaves as the component does in Power Apps. In Studio it uses Microsoft's controls, which can look slightly different.
Add it in five steps
- In Power Apps Studio, open Settings > Updates and check that Modern controls and themes and Enhanced component properties are on.
- Open the Components tab in the tree view and select New component.
- Select an empty part of Studio, so the new component isn't selected.
- Copy the YAML on this page and press Ctrl+V.
lcsChatThreadappears with all its properties. - On a screen, insert it from Insert > Custom >
lcsChatThread.
What it needs
- No premium licence: standard controls, no connectors
- Modern controls and themes: on
- Enhanced component properties: on
- Tested in Power Apps Studio 3.26094.9 on November 2, 2026
- Version 0.1.0
Properties (21)
Read from the component's own YAML, so they always match what you paste.
| Property | Kind | Type | Default |
|---|---|---|---|
MessagesThe messages, as a table with Key, Text, From, When (a date and time), Status (sent, delivered, read or failed) and Initials columns, oldest first. | Input | Table | Table({Key: "m1", Text: "Hi, the projector in room 4 won't connect. Can someone take a look before the 2 o'clock?", From: "Dana Kaur", When: DateAdd(Now(), -42, TimeUnit.Minutes), Status: "read", Initials: "DK"}, {Key: "m2", Text: "On it. Is it the HDMI or the wireless?", From: "me", When: DateAdd(Now(), -40, TimeUnit.Minutes), Status: "read", Initials: ""}, {Key: "m3", Text: "Wireless. The HDMI works but the cable is too short for the lectern.", From: "Dana Kaur", When: DateAdd(Now(), -38, TimeUnit.Minutes), Status: "read", Initials: "DK"}, {Key: "m4", Text: "I'll bring a longer cable and reset the receiver. Ten minutes.", From: "me", When: DateAdd(Now(), -2, TimeUnit.Minutes), Status: "delivered", Initials: ""}) |
MeThe From value that is the person using the app; those messages sit on the right in your accent colour. | Input | Text | "me" |
IsTypingShows a bubble of bouncing dots under the last message, for when the other side is typing or an agent is thinking. | Input | Boolean | false |
TypingNameWho is typing, shown beside the dots. Empty shows the dots alone. | Input | Text | "" |
PlaceholderThe hint in the composer. | Input | Text | "Write a message" |
ShowComposerShows the composer with its Send button. Off, the thread is read-only. | Input | Boolean | true |
ShowAvatarsShows a circle of initials beside the other side's messages, on the first message of each run. | Input | Boolean | true |
NewestFirstPuts the newest message at the top, where ScrollToLatest can always reach it. Off, the newest is at the bottom, as chats usually read. | Input | Boolean | false |
MaxHeightThe height of the messages area. Longer threads scroll inside it. | Input | Number | 360 |
SizeThe text size of the messages. | Input | Number | 14 |
AccentColorYour brand colour, for your own bubbles, the Send button and the focus ring. | Input | Color | RGBA(15, 108, 189, 1) |
ThemeLight, or Dark for a dark screen. | Input | Text | "Light" |
MotionA new bubble pops in, and the typing dots bounce. None plays for people who've asked their device for reduced motion. | Input | Boolean | true |
| Property | Kind | Type | Default |
|---|---|---|---|
DraftWhat's typed in the composer and not sent yet, trimmed. | Output | Text | — |
CountHow many messages the thread has. | Output | Number | — |
LastKeyThe Key of the latest message by When. | Output | Text | — |
| Property | Kind | Type | Default |
|---|---|---|---|
OnSend(Text)Runs in your app when Send is selected with text in the composer. Add the message to your table here; the composer clears itself.
| Event | — | false |
OnRetry(Key, Text)Runs in your app when Retry is selected on a message that wasn't sent, with its key and text.
| Event | — | false |
| Property | Kind | Type | Default |
|---|---|---|---|
SetDraft(Text)Puts text in the composer from your app, such as a suggested reply.
| Action | — | — |
ClearDraft()Empties the composer. | Action | — | — |
ScrollToLatest()Scrolls the messages back to their first row. With NewestFirst on, that is the latest message. | Action | — | — |
Guide
Use lcsChatThread for a conversation on a case, a help desk chat, the back-and-forth with an agent built in Copilot Studio, or any screen where two sides take turns. The usual build is a gallery of labels that all look the same; this component gives the conversation its shape:
- Bubbles from a table of
Key,Text,From,When,StatusandInitials: yours (Fromequal toMe) on the right in your accent colour, theirs on the left in grey with a circle of initials on the first message of each run; the bubbles are well rounded, with a nearly square bottom corner on the sender's side for the first bubble of a run; - Delivery state under your messages: sent, delivered, read, or not sent with Retry, which raises
OnRetry(Key, Text); - A typing indicator of bouncing dots, with a name, for when the other side is typing or an agent is thinking;
- A composer with a Send button that raises
OnSend(Text)and clears itself;SetDraftfor a suggested reply,Draftout while they type; - Newest at the bottom as chats read, or
NewestFirstsoScrollToLatestalways reaches the latest.
The examples call it chatCase on a case screen with the messages in a collection.
Load the messages and send one:
// Screen.OnVisible
ClearCollect(colChat, ShowColumns(Filter('Case Messages', Case.Case = varCase.Case), "Key", "Text", "From", "When", "Status", "Initials"))
// chatCase.Messages
colChat
// chatCase.OnSend
With(
{key: GUID()},
Collect(colChat, {Key: Text(key), Text: Text, From: "me", When: Now(), Status: "sent", Initials: ""});
IfError(
Patch('Case Messages', Defaults('Case Messages'), {Key: Text(key), Text: Text, Case: varCase, When: Now()}),
Patch(colChat, LookUp(colChat, Key = Text(key)), {Status: "failed"}),
Patch(colChat, LookUp(colChat, Key = Text(key)), {Status: "delivered"})
)
)
Retry a message that didn't send:
// chatCase.OnRetry
Patch(colChat, LookUp(colChat, Key = Key), {Status: "sent"});
IfError(
Patch('Case Messages', Defaults('Case Messages'), {Key: Key, Text: Text, Case: varCase, When: Now()}),
Patch(colChat, LookUp(colChat, Key = Key), {Status: "failed"}),
Patch(colChat, LookUp(colChat, Key = Key), {Status: "delivered"})
)
An agent's reply, with the dots while it thinks:
// chatCase.OnSend, talking to a Copilot Studio agent through a flow
Collect(colChat, {Key: Text(GUID()), Text: Text, From: "me", When: Now(), Status: "read", Initials: ""});
Set(varThinking, true);
Collect(colChat, {Key: Text(GUID()), Text: AskAgent.Run(Text).reply, From: "Agent", When: Now(), Status: "", Initials: "AI"});
Set(varThinking, false)
// chatCase.IsTyping and chatCase.TypingName
varThinking
"Agent"
A suggested reply:
// btnThanks.OnSelect
chatCase.SetDraft("Thanks, that's sorted.")
- Columns.
Messageswants exactlyKey,Text,From,When,StatusandInitials; build them withShowColumnsandRenameColumns, or aForAll, as in Use it.Whenmust be a date and time. - Who is "me". Set
Meto what yourFromcolumn holds for the signed-in person:User().FullName, or"me"if you write that when you collect. - Keep the collection small. A case's messages are few; for a long-running chat, load the last hundred and add a "Load earlier" button of your own that collects more.
- The messages are a group named "Conversation". Each message's text and its time and state are real text, read in order; a message that wasn't sent has a Retry button named with the message. The composer is a text box named by its placeholder, and Send is a button that is disabled until there's text.
- Your messages and theirs differ by side and colour, and by the state line under yours, so the thread reads correctly without colour.
- With
Motionon, the latest bubble pops in and the typing dots bounce. None plays for people who've asked their device for reduced motion, andMotionoff draws them at once.
- The messages area is
MaxHeighttall even with a few messages: Studio can't size a component from what's inside it (it falls back to a default size), so longer threads scroll inside it rather than the component growing. - Bubble width is estimated from the message's length, because a formula can't measure text; a message of many short lines gets a wider bubble than it needs. Rows grow to fit the text.
- Power Apps can't scroll a gallery to its end by formula, so with the newest message at the bottom, people scroll to it;
ScrollToLatestgoes to the first row, which is the latest only withNewestFirston. KeepMaxHeightgenerous, or put the component on a scrollable screen. - Enter doesn't send: the modern text input raises its change event when focus leaves it, not on Enter, so Send is the button.
- Messages are plain text; links and formatting don't render.
- 0.1.0: first version: bubbles from a table, delivery state with Retry, the typing indicator, the composer with
OnSend,SetDraft,ClearDraftandScrollToLatest, the outputs, light and dark, the bubble and dots motion.
Did it work for you?
No ratings yet.Nobody has said it worked in their app yet.
Sign in to rate it or to say it worked in your app.
Comments
No comments yet. Share how you used it, or ask something.
Sign in to comment or reply. Comments show under a display name you choose, never your email.