Skip to content

AI

AiChatMessage

Bit.BlazorUI.AIMessageBubble

BitAiChatMessage is one message of a conversation, laid out by role: a user turn as a bubble at the end of the line (with its attachments, copy and edit), an assistant turn as full-width prose with its parts - text, Markdown, reasoning, tool calls, artifacts, images, generative UI - its error, its sources and its actions, and a system note as a centered pill. It is what BitAiMessageList renders for every message, and what a MessageTemplate wraps to keep all of that.

Notes

To use this component, you need to install the Bit.BlazorUI.AI(opens in a new tab) nuget package, as described in the Optional steps of the Getting started page.

The message subscribes to its BitAiMessage for structural changes only (a part added, the status moving): the content of a part is re-rendered by the part's own component, so a streaming token never re-renders the whole message. A Session enables editing, regenerating, retrying and approving; without one the message only shows. What it shows under a reply - the actions, the model, the usage, the time, the sources - follows the BitAiUiOptions in force.

The message is an article, aria-busy while it is generated so a screen reader waits for it to settle - but for a reply waiting on the user's decision on a tool call, whose question a busy message would keep from being announced - and carries data-message-id and data-role for an app to find one message or style a role of its own. A user's text is shown as it was typed - never parsed as Markdown or HTML. The entrance (Animate) follows the motion tokens, so it is skipped under reduced motion; use the ForceAnimation toggle at the top of this page to see it.

Usage

Every example is live. Open its code to see exactly what produced the component running underneath.

Basic

A Message is laid out by its role: the user's as a bubble at the end of the line, under the files it carries (its Attachments, as chips), the assistant's as prose with its actions (revealed on hover) and the model and usage under it, a system message as a centered note. Without a Session a reply offers copy and feedback only, and a user message no action at all. The model and the usage show because this site's options turn ShowModelName and ShowUsage on.

layout.pdf178 KB
Can you lay a message out by its role?

Yes. A reply is full-width prose, with copy and feedback under it - hover it to see them.

Demo Large138 tokensInput: 42 Output: 96
The assistant joined the conversation.

Parts

A reply is a list of parts, each drawn by its own view: plain text, Markdown with code, a tool call with its result, a table with a citation and the sources under it. Conversation is what resolves the artifacts a part refers to.

Answer 1. The cost is dominated by rendering, not by parsing. Coalescing updates removes most of it, and virtualization removes the rest.

Demo Large180 tokensInput: 120 Output: 60

Here is the shape of it:

csharp
var budget = TimeSpan.FromMilliseconds(18);
scheduler.Request();

The interval is the only dial that matters.

Demo Large184 tokensInput: 122 Output: 62
Searched the documentation
scale 4
  • Render modes 94%
  • Circuit lifetime 81%

Two passages matched, and they say the same thing.

Demo Large188 tokensInput: 124 Output: 64
MessagesRenderedNotes
60allbelow the threshold
500visible onlyvirtualized
Sources
1
Performance notes
Internal wiki
Demo Large192 tokensInput: 126 Output: 66

Session

With a Session, a user message can be edited (hover it) and the reply marked IsLatest regenerated - only the newest, since regenerating an older one discards everything after it. This transport fails its first answer: the error offers to try again, which the session runs as a retry (RetryAsync). An edit starts a turn of its own, so it waits while a reply is being generated; Escape cancels it.



Animate

Animate plays the entrance of a message that has just arrived. Leave it off for the messages restored from history - BitAiMessageList turns it on only for the ones added while it is shown.



Add a few messages below.

Child content

ChildContent is rendered at the end of an assistant message, after its parts and sources: a follow-up action of the app's own, here.

Your order shipped this morning and should arrive on Thursday.

Demo Large138 tokensInput: 42 Output: 96

The order has not been opened yet.

Citations & feedback

OnCitationClick reports a numbered citation chip, OnSourceClick a source card and OnFeedback a thumbs up or down - pressing the chosen one again withdraws it, reported as None.

On the server, for as long as the circuit lives. When the circuit drops, the state goes with it.

Sources
1
Blazor render modes
Microsoft Learn
2
SignalR circuit lifetime
Microsoft Learn
Demo Large138 tokensInput: 42 Output: 96

Last event: -

Disabled

A disabled message stays readable; its actions, its editor and its retry are disabled with it.

Hover me: my copy and edit buttons are disabled too.

And so are the actions under this reply.

Demo Large138 tokensInput: 42 Output: 96

Cascading parameters

BitParams with a BitAiChatMessageParams sets the classes and styles of every message inside it - the messages a BitAiMessageList or a BitAiChat draws included, since neither sets them itself; a message's own value wins, as a whole (the third keeps its own Styles).

My bubble takes its style from the BitParams around me.

So does the body of this reply.

Demo Large138 tokensInput: 42 Output: 96
I keep my own Styles, so none of the cascaded ones reach me.

Style & Class

Style / Class reach the root, Styles / Classes each part (the bubble, the body, the system pill, the actions and the editor, with the class-styles of the buttons and of the BitAiMessageActions they render). The public --bit-AiChatMessage-* variables (see the CSS variables table) re-skin the messages from :root, an ancestor or Style.

A bubble with a style of its own.

A reply with a class on its root and its body.

Demo Large138 tokensInput: 42 Output: 96
A bubble restyled by the variables of its container.

So is this note.

RTL

Use BitAiChatMessage in right-to-left (RTL).

می‌توانی پیام را بر اساس نقشش بچینی؟

بله. پاسخ به صورت متن تمام‌عرض نمایش داده می‌شود و دکمه‌های کپی و بازخورد زیر آن هستند.

Demo Large138 tokensInput: 42 Output: 96

CSS variables

The public custom properties this component reads off its root, for what no parameter covers.

Every variable is read with a fallback and never declared by the component, so it inherits like any other custom property: set one on :root (or in a [bit-theme] block) to restyle every instance, on any ancestor to restyle the ones inside it, or on the Style of one instance to restyle it - and any instance nested inside it, which inherits the value too; a parameter changes one alone, as does the real CSS property on the instance's Style / Styles when that property does not inherit (opacity: 1 keeps one disabled instance undimmed without undimming a disabled one inside it), while an inherited one such as color or font-family also reaches the nested instances that do not set it themselves. Leave a variable unset and the component falls back to the theme token beside it.

BitAiChatMessage CSS variables

Name Default value Description
--bit-AiChatMessage-gap spacing(1) Room between the parts of a message: its attachments, its bubble or body, its actions.
--bit-AiChatMessage-user-background var(--bit-clr-bg-sec) Background of the bubble of a user message.
--bit-AiChatMessage-user-color var(--bit-clr-fg-pri) Text of the bubble of a user message.
--bit-AiChatMessage-user-max-width spacing(80), the full width on a phone Width the bubble of a user message stops growing at.
--bit-AiChatMessage-user-radius var(--bit-shp-radius-lg) Corners of the bubble of a user message, less the one it speaks from, which stays tighter.
--bit-AiChatMessage-assistant-background transparent Background of the body of an assistant message.
--bit-AiChatMessage-assistant-color var(--bit-clr-fg-pri) Text of the body of an assistant message.
--bit-AiChatMessage-body-gap spacing(1.5) Room between the parts of an assistant reply.
--bit-AiChatMessage-system-background var(--bit-clr-bg-sec) Background of the pill of a system message.
--bit-AiChatMessage-system-color var(--bit-clr-fg-sec) Text of the pill of a system message.
--bit-AiChatMessage-system-border-color var(--bit-clr-brd-ter) Border of the pill of a system message.

API

Every parameter, public member, sub-class and enum this component exposes.

BitAiChatMessage parameters

Name Type Default value Description
Animate bool false Plays the entrance animation of the message - for one that has just arrived, never for the messages restored from history. It follows the motion tokens, so it is skipped under reduced motion.
ChildContent RenderFragment? null The extra content rendered at the end of an assistant message, after its parts, its error and its sources.
Classes BitAiChatMessageClassStyles? null Custom CSS classes for different parts of the chat message.
Conversation BitAiConversation? null The conversation the message belongs to, which resolves the artifacts its parts refer to.
IsLatest bool true Whether this is the newest message in the transcript. Regeneration is offered only for the newest reply, because regenerating an older one discards everything after it. It defaults to true, so a reply drawn on its own with a Session offers it; a transcript drawn by hand sets it on the last message only, as BitAiMessageList does.
Message BitAiMessage The message to render (required). Its Role decides the layout: a user bubble, an assistant reply or a system note.
OnCitationClick EventCallback<BitAiCitation> The callback for when the user activates a numbered citation in the message.
OnFeedback EventCallback<BitAiMessageFeedback> The callback for when the user records feedback on this message (or withdraws it), so a host can persist it. The message's Feedback is already updated when it is raised.
OnSourceClick EventCallback<BitAiCitationSource> The callback for when the user activates a source card in the list of sources under the message.
Session BitAiChatSession? null The session driving the conversation, which enables editing a user message, regenerating and retrying a reply, and deciding the approval of a tool call or the question of an agent run.
Styles BitAiChatMessageClassStyles? null Custom CSS styles for different parts of the chat message.

BitComponentBase parameters

Name Type Default value Description
AriaLabel string? null Gets or sets the accessible label for the component, used by assistive technologies.
Class string? null Gets or sets the CSS class name(s) to apply to the rendered element.
Dir BitDir? null Gets or sets the text directionality for the component's content.
Disabled bool false Gets or sets a value indicating whether the component is disabled and cannot respond to user interaction.
ForceAnimation bool false Gets or sets a value indicating whether the component's animations play at their full duration even when reduced motion is requested.
HtmlAttributes Dictionary<string, object> new Dictionary<string, object>() Captures additional HTML attributes to be applied to the rendered element, in addition to the component's parameters. An attribute written on the component wins over an entry of the same name.
Id string? null Gets or sets the unique identifier for the component's root element.
Style string? null Gets or sets the CSS style string to apply to the rendered element.
TabIndex string? null Gets or sets the tab order index for the component when navigating with the keyboard.
Visibility BitVisibility BitVisibility.Visible Gets or sets the visibility state (visible, hidden, or collapsed) of the component.

BitComponentBase public members

Name Type Default value Description
UniqueId Guid Guid.NewGuid() Gets the readonly unique identifier for the component's root element, assigned when the component instance is constructed.
RootElement ElementReference Gets the reference to the root HTML element associated with this component.

BitAiChatMessageClassStyles properties

Name Type Default value Description
Root string? null Custom CSS classes/styles for the root element of the BitAiChatMessage.
System string? null Custom CSS classes/styles for the pill a system message is shown in by the BitAiChatMessage.
Attachments string? null Custom CSS classes/styles for the attachments of a user message of the BitAiChatMessage.
Bubble string? null Custom CSS classes/styles for the bubble a user message is shown in by the BitAiChatMessage.
Actions string? null Custom CSS classes/styles for the actions of a user message (copy and edit) of the BitAiChatMessage.
EditButton BitButtonClassStyles? null Custom CSS classes/styles for the edit button of a user message of the BitAiChatMessage.
Editor string? null Custom CSS classes/styles for the editor a user message is edited in by the BitAiChatMessage.
EditorActions string? null Custom CSS classes/styles for the row of buttons under the editor of the BitAiChatMessage.
CancelButton BitButtonClassStyles? null Custom CSS classes/styles for the cancel button of the editor of the BitAiChatMessage.
SaveButton BitButtonClassStyles? null Custom CSS classes/styles for the save button of the editor of the BitAiChatMessage.
Body string? null Custom CSS classes/styles for the body of an assistant message of the BitAiChatMessage.
MessageActions BitAiMessageActionsClassStyles? null Custom CSS classes/styles for the BitAiMessageActions under an assistant message of the BitAiChatMessage.

BitAiMessage properties

A single message: a role, an ordered list of heterogeneous parts, and the metadata around them. Mutable and observable: streaming mutates it in place and raises Changed for structural changes and PartChanged for content changes inside a part, so a token re-renders one small subtree. Build one with new BitAiMessage(role, messageId?) and AddPart, or let BitAiConversation.AddMessage(role, text) do it; the parts, attachments and citations are copy-on-write snapshots, safe to read while a stream writes.

Name Type Default value Description
MessageId string The identifier of the message, given to its constructor or generated.
Role BitAiRole Who authored it: BitAiRole.User, Assistant, System, Tool or Developer (a string value, so a provider's own role fits too).
CreatedAt DateTimeOffset DateTimeOffset.UtcNow When the message was created, which a ShowTimestamps option shows under it.
Parts IReadOnlyList<BitAiMessagePart> [] The parts the message is made of, in order: BitAiTextPart, BitAiMarkdownPart, reasoning, tool calls, artifacts, images, generative UI and the rest.
Attachments IReadOnlyList<BitAiAttachment> [] The files the message carries, shown as chips above a user bubble.
Citations IReadOnlyList<BitAiCitation> [] The citations of the message, each numbered by AddCitation (a source cited twice keeps its number).
Status BitAiMessageStatus BitAiMessageStatus.Complete Where the message is in its lifecycle. A Pending one with no part yet shows a thinking indicator.
IsBusy bool false True while the message is pending or streaming: its actions are hidden and it is aria-busy.
Metadata BitAiGenerationMetadata? null What the provider reported about the generation: the model, the token usage, the finish reason, the timings. Merged with MergeMetadata.
Error BitAiError? null The failure that ended the message, shown with a retry when it is retryable.
StatusText string? null A transient status line shown while the message is generating ("Searching the documentation").
Feedback / FeedbackComment BitAiFeedbackKind / string? BitAiFeedbackKind.None / null The sentiment the user attached to the message, and the free text that came with it.
Run BitAiAgentRun? null The agent run the message reports, drawn above its parts.
Extensions Dictionary<string, object?> [] Application-defined values attached to the message.
Version int 0 A number that goes up on every change of the message, structural or inside a part, for a cheap check of whether anything changed since it was last read.
AddPart(part) / InsertPart(index, part) / RemovePart(partId) TPart / TPart / bool Adds a part at the end or at a position, or removes one.
FindPart(partId) / FindPart<TPart>(partId) / PartsOf<TPart>() / FindToolCall(toolCallId) BitAiMessagePart? / TPart? / IEnumerable<TPart> / BitAiToolCallPart? Finds a part by its identifier (of a type, with the generic one), enumerates the parts of a type, or finds a tool call anywhere in the message.
GetOrCreateOpenPart<TPart>(kind, factory) / CompleteOpenParts() TPart / void What a stream writes through: the last part when it is of that kind and still open, else a new one the factory creates and appends - so text after a tool call starts a new paragraph below it. CompleteOpenParts closes every part still open, for a message that ends abruptly.
AddAttachment(attachment) / RemoveAttachment(attachmentId) void / bool Adds or removes a file of the message.
AddCitation(citation) / GetSources() BitAiCitation / IReadOnlyList<BitAiCitationSource> Adds a citation, deduplicated by source and given the next marker number (returned with it); GetSources lists the distinct sources cited, in marker order.
SetStatus(status) / SetStatusText(text) / SetError(error) void Moves the message to a status, sets its transient status line, or records the failure that ended it.
MergeMetadata(metadata) void Merges generation metadata, keeping the existing values wherever the incoming record leaves a field null - providers report it piecemeal.
SetFeedback(feedback, comment?) void Records the user's sentiment about the message, with an optional free-text comment (FeedbackComment).
GetText() string The text of the message: the text of its parts, the reasoning left out, joined by a blank line - what the copy button copies.
Changed / PartChanged event Action<BitAiMessage>? / event Action<BitAiMessage, BitAiMessagePart>? Raised after a structural change (a part added or removed, the status moving), and after a part reports a content change.

BitAiRole properties

Who authored a message: a readonly record struct around a string value, compared without regard to case, so a provider's own role converts to one implicitly.

Name Type Default value Description
User / Assistant / System / Tool / Developer BitAiRole The well-known roles, "user", "assistant", "system", "tool" and "developer".
Value string The role as a string, which the message writes into its data-role attribute.
IsUser / IsAssistant / IsSystem / IsTool bool Which of the well-known roles it is. IsSystem is true for the developer role too: both are instructions to the model rather than turns of the conversation.

BitAiMessageFeedback properties

A feedback decision the user made on a message, raised by the OnFeedback of BitAiChatMessage, BitAiMessageList and BitAiChat so the host can persist it. The BitAiMessageActions under a reply reports only the BitAiFeedbackKind; the message pairs it with itself. A sealed record, compared by value.

Name Type Default value Description
Message BitAiMessage The message the feedback is about. Its Feedback is already updated.
Feedback BitAiFeedbackKind The new sentiment, or None when the user withdrew it.

BitAiMessageStatus enum

Name Value Description
Draft 0 Composed locally and not yet handed to a transport.
Pending 1 Submitted; the session is waiting for the first event. A reply with no part yet shows a thinking indicator.
Streaming 2 Events are arriving and the message is growing.
Complete 3 The message finished normally.
Cancelled 4 The message was cancelled by the user or the host; its partial content is kept.
Failed 5 The message terminated with an error.

BitVisibility enum

Name Value Description
Visible 0 The content of the component is visible.
Hidden 1 The content of the component is hidden, but the space it takes on the page remains (visibility:hidden).
Collapsed 2 The component is hidden (display:none).

BitDir enum

Name Value Description
Ltr 0 Ltr (left to right) is to be used for languages that are written from the left to the right (like English).
Rtl 1 Rtl (right to left) is to be used for languages that are written from the right to the left (like Arabic).
Auto 2 Auto lets the user agent decide. It uses a basic algorithm as it parses the characters inside the element until it finds a character with a strong directionality, then applies that directionality to the whole element.

Feedback

Found a mistake, a gap, or something that could be clearer? Every page and every component is one click from its source.