Introduction
Message bubbles may seem like a trivial UI element in AI applications, yet they grow highly complex once put into production. Typical requirements include token-by-token streaming output, Markdown rendering, image display, multi-modal content, reasoning process unfolding, tool call visualization, message grouping, collapsible states, role-specific styles, auto-scrolling, and more. Implementing all of these features from scratch bloats business code with tedious presentation logic.
The Bubble component of TinyRobot is purpose-built to solve this pain point. Rather than a simple text bubble, it delivers a complete message rendering system for AI chat interfaces, consisting of three core primitives: Bubble, BubbleList and BubbleProvider. Developers can seamlessly scale from rendering single messages to full conversation streams.
Render One AI Message with Minimal Code
Basic usage is straightforward:
<template>
<tr-bubble
role="assistant"
content="Hello, I'm TinyRobot, here to help you quickly build AI chat interfaces."
placement="start"
/>
</template>
<script setup lang="ts">
import { TrBubble } from "@opentiny/tiny-robot";
</script>
The placement prop controls left/right alignment, while the avatar slot accepts custom avatar components. Visual styles such as background color, font size, border radius and width can be customized via CSS variables. This allows teams to rapidly build functional UIs and refine styling to match product designs later.
Reactive Content Optimized for Streaming Output
AI responses are returned incrementally token by token instead of in one full payload. The content property of Bubble is fully reactive — simply update its value continuously to achieve native streaming effects.
<script setup lang="ts">
import { TrBubble } from "@opentiny/tiny-robot";
import { IconAi } from "@opentiny/tiny-robot-svgs";
import { h, ref } from "vue";
const aiAvatar = h(IconAi, { style: { fontSize: "32px" } });
const streamContent = ref("");
async function startStream() {
const fullText = "This is an AI response being generated token by token.";
streamContent.value = "";
for (const char of fullText) {
streamContent.value += char;
await new Promise((resolve) => setTimeout(resolve, 80));
}
}
</script>
This architecture works perfectly with SSE, Fetch Stream, or the message management utilities from TinyRobot Kit. The presentation layer only reacts to changes in message data without embedding streaming logic inside the component itself.
Support for More Than Text: Images, Markdown, Reasoning & Tool Calls
The Bubble content model aligns with standard LLM message structures. The content prop accepts either plain strings or structured arrays for multi-modal content, such as images:
<tr-bubble
content-render-mode="split"
:content="[
{ type: 'text', text: 'Here is the generated result:' },
{ type: 'image_url', image_url: { url: imageUrl } }
]"
/>
When items carry the image_url type, the built-in image renderer activates automatically. Use contentRenderMode to render all media within a single bubble box or split them into separate blocks.
Pre-built renderers are included for all common AI output types:
- Text: Plain text rendering
- Image: Image asset rendering
- Markdown: Full Markdown syntax support
- Loading: Placeholder loading state
- Reasoning: Collapsible model thinking process
- Tools / Tool: Single or batch function call visualization
- ToolRole: Dedicated styling for tool response messages
Render reasoning content by passing the dedicated reasoning_content prop:
<tr-bubble
:content="answer"
:reasoning_content="reasoningContent"
:state="{ thinking: false, open: true }"
/>
Tool calls follow the standard OpenAI schema format:
const message = {
role: "assistant",
content: "I will look up the weather for you.",
tool_calls: [
{
id: "call_0",
type: "function",
function: {
name: "get_weather",
arguments: '{"city":"Shenzhen"}'
}
}
],
state: {
toolCall: {
call_0: { status: "running", open: true }
}
}
};
This design makes Bubble ideal for building Agents, Copilots, enterprise knowledge base assistants, and other products that need transparent visibility into model execution steps.
BubbleList: Scale from Single Bubbles to Full Conversation Flows
Real-world chat interfaces render sequences of messages. The BubbleList component accepts a messages array and unifies avatar, alignment and visibility rules across roles via roleConfigs.
<template>
<tr-bubble-list
auto-scroll
:messages="messages"
:role-configs="roleConfigs"
/>
</template>
<script setup lang="ts">
import type { BubbleListProps, BubbleRoleConfig } from "@opentiny/tiny-robot";
import { TrBubbleList } from "@opentiny/tiny-robot";
const messages: BubbleListProps["messages"] = [
{ role: "user", content: "Summarize this document for me" },
{ role: "assistant", content: "Sure, please upload your document." }
];
const roleConfigs: Record<string, BubbleRoleConfig> = {
user: { placement: "end" },
assistant: { placement: "start" }
};
</script>
The default grouping strategy uses user messages as dividers: every user message starts a new group, and all subsequent non-user messages are merged into the same reply block. Two alternative grouping modes are available: consecutive role grouping, or fully custom grouping functions.
This grouping logic fits standard AI chat workflows naturally:
User Message
└─ Assistant Reply Group
├─ Assistant: Initiate data lookup via tool calls
├─ Tool: Return ticket details
├─ Tool: Return SLA policy rules
└─ Assistant: Risk analysis based on tool outputs
User Message
└─ Assistant Reply Group
├─ Assistant: Fetch assignee and approval status
├─ Tool: Return handler information
└─ Assistant: Recommended resolution steps
The autoScroll feature is optimized for chat UX:
- Scrolls smoothly to the bottom when users send new messages
- Only follows streaming updates if the user is already viewing the bottom, avoiding disruption while browsing history.
Renderer Architecture: Extend Content Types Without Rewriting Components
One of Bubble’s core advantages is its pluggable renderer system. Render logic is split into two independent layers:
- Box Renderer: Controls the outer bubble container layout and styling
- Content Renderer: Handles internal rendering of text, media, tool outputs, and custom widgets
Register global renderers across your entire component tree with BubbleProvider:
<tr-bubble-provider :content-renderer-matches="contentRendererMatches">
<tr-bubble-list :messages="messages" />
</tr-bubble-provider>
import { BubbleRendererMatchPriority, type BubbleContentRendererMatch } from "@opentiny/tiny-robot";
import { markRaw } from "vue";
import SchemaCardRenderer from "./SchemaCardRenderer.vue";
const contentRendererMatches: BubbleContentRendererMatch[] = [
{
find: (_, content) => content.type === "schema_card",
renderer: markRaw(SchemaCardRenderer),
priority: BubbleRendererMatchPriority.CONTENT
}
];
This pattern lets teams inject custom structured widgets — order cards, approval panels, knowledge base citations, charts — without forking component source or writing massive conditional rendering logic inside message lists.
State Isolation Optimized for Enterprise AI Systems
All transient UI states (expanded reasoning panels, unfolded tool call details, like/dislike feedback) are stored separately in the state field of each message. State change events are emitted via state-change to parent components.
This separation keeps raw LLM message payloads clean and serializable for API transmission or database persistence, without polluting core business data with temporary UI flags.
Summary
TinyRobot Bubble is far more than a set of aesthetic chat bubbles. It encapsulates all repetitive, complex presentation logic common to AI chat interfaces into a composable system:
- Single message rendering:
Bubble - Full conversation stream rendering:
BubbleList - Global custom content extension:
BubbleProvider - Native built-in support for text, images, Markdown, reasoning chains and tool calls
- Full coverage of role styling, message grouping, auto-scrolling, slots and complete TypeScript typing
If you build AI Chat, Agent consoles, enterprise knowledge assistants or Copilot products with Vue 3, TinyRobot Bubble frees you from low-level UI implementation work to focus on core AI user value.
About OpenTiny NEXT
OpenTiny NEXT is an enterprise intelligent front-end solution built on two core technologies: Generative UI and WebMCP. It intelligently upgrades legacy products including the TinyVue component library and TinyEngine low-code engine, while launching new Agent-oriented products: NEXT-SDKs for frontend, AI Extension, TinyRobot AI Assistant and GenUI. The suite empowers AI to interpret user intentions and execute tasks autonomously, accelerating intelligent transformation for enterprise applications.
Join the OpenTiny Open Source Community
WeChat Assistant: opentiny-official
Official Website: https://opentiny.design
TinyRobot Repository: https://github.com/opentiny/tiny-robot (Star ⭐ appreciated)
To contribute, look for issues tagged good first issue in the repository. Feel free to leave comments with any questions or feedback!



Top comments (0)