Skip to main content
RapidDev - Software Development Agency
bubble-tutorial

How to Build a Chat System in Bubble

Building a chat system in Bubble involves creating Conversation and Message Data Types, a contacts list with unread badges, a message thread that auto-updates via Bubble's real-time search, and workflows for sending messages. This tutorial covers the complete data model, chat UI layout with a Repeating Group for messages, typing indicators using custom states, unread message counting, and auto-scrolling to the latest message.

What you'll learn

  • How to design the data model for conversations and messages
  • How to build the chat UI with message list and input area
  • How to implement real-time message delivery using Bubble's auto-updating searches
  • How to add unread message counts and typing indicators
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner7 min read30-40 minAll Bubble plansMarch 2026RapidDev Engineering Team
TL;DR

Building a chat system in Bubble involves creating Conversation and Message Data Types, a contacts list with unread badges, a message thread that auto-updates via Bubble's real-time search, and workflows for sending messages. This tutorial covers the complete data model, chat UI layout with a Repeating Group for messages, typing indicators using custom states, unread message counting, and auto-scrolling to the latest message.

Overview: Chat System in Bubble

This tutorial guides you through building a complete one-on-one and group messaging system in Bubble. You will create the database structure, build the chat interface, implement real-time updates, and add polish features like unread counts and typing indicators.

Prerequisites

  • A Bubble app with user authentication set up
  • Basic understanding of Data Types and Repeating Groups
  • Familiarity with Bubble Workflows and custom states
  • Users registered in your app for testing

Step-by-step guide

1

Design the chat data model

Go to the Data tab and create two Data Types. First, 'Conversation' with fields: 'participants' (list of Users), 'last_message' (text — preview of the most recent message), 'last_message_time' (date), and 'name' (text — for group chats, optional). Second, 'Message' with fields: 'conversation' (Conversation), 'sender' (User), 'body' (text), 'is_read' (yes/no, default no), and the auto-generated Created Date serves as the timestamp. This two-table structure keeps conversation metadata separate from individual messages for efficient querying.

Pro tip: Store last_message and last_message_time on the Conversation to avoid searching Messages just to display conversation previews in the list.

Expected result: Conversation and Message Data Types are created with all required fields and relationships.

2

Build the conversation list sidebar

In the Design tab, create a two-column layout: a sidebar on the left (300px wide) and a main chat area on the right. In the sidebar, add a Repeating Group with type Conversation. Set the data source to Do a Search for Conversation where participants contains Current User, sorted by last_message_time descending. Inside each cell, display the other participant's name, the last message preview, and the time. Add a conditional badge showing the count of unread messages: Do a Search for Message where conversation = current cell's Conversation and is_read = no and sender is not Current User, then display the count.

Expected result: A sidebar shows all conversations the current user is part of, sorted by most recent, with unread message badges.

3

Build the message thread area

In the main chat area, add a Repeating Group with type Message. Set the data source to Do a Search for Message where conversation = the selected conversation from the sidebar, sorted by Created Date ascending. Inside each cell, create a Row group. Use conditionals to align messages: When current cell's Message's sender is Current User → align right with a blue background. When sender is not Current User → align left with a gray background. Display the message body, sender name, and timestamp. Below the Repeating Group, add an Input element for typing new messages and a Send button.

Expected result: Messages display in chronological order with the current user's messages on the right and other participants' messages on the left.

4

Create the send message workflow

Add a workflow: When Send button is clicked (or when the user presses Enter in the input). Add these actions in order: Create a new Message with body = Input's value, sender = Current User, conversation = the selected conversation. Then Make changes to the Conversation: set last_message = Input's value and last_message_time = Current date/time. Finally, clear the input by resetting it. Bubble's auto-updating search on the message Repeating Group will automatically display the new message for all conversation participants without any additional real-time setup.

Expected result: Sending a message creates a Message record, updates the conversation preview, and the message appears in real-time for all participants.

5

Add typing indicators and auto-scroll

For typing indicators, add a field to Conversation called 'typing_users' (list of Users). When the message Input's value changes, add Current User to the typing_users list. Use a 'Do when condition is true' event to remove the user after 3 seconds of inactivity. Display a '... is typing' text below the message list that shows when typing_users is not empty and does not contain only Current User. For auto-scrolling, use the Scroll to entry action on the Repeating Group after a new message is created, targeting the last entry. Add this as a step in the send message workflow.

Pro tip: Use Schedule a custom event with a 3-second delay to clear the typing indicator rather than a continuously running check, which saves WUs.

Expected result: Users see a typing indicator when the other person is writing, and the message list auto-scrolls to show new messages.

6

Mark messages as read

When a user opens a conversation or the conversation's message list loads, mark all unread messages as read. Create a workflow on the conversation list click event: after setting the selected conversation, add an action to Make changes to a list of things. Target Do a Search for Message where conversation = selected conversation, is_read = no, and sender is not Current User. Set is_read to yes. This updates the unread count badge in the sidebar. Also trigger this workflow when the page loads if a conversation is already selected.

Expected result: Opening a conversation marks all unread messages from other participants as read, clearing the unread badge.

Complete working example

Workflow summary
1CHAT SYSTEM DATA MODEL AND WORKFLOWS
2=====================================
3
4DATA TYPES:
5 Conversation:
6 - participants (list of Users)
7 - last_message (text)
8 - last_message_time (date)
9 - name (text, optional for groups)
10 - typing_users (list of Users)
11
12 Message:
13 - conversation (Conversation)
14 - sender (User)
15 - body (text)
16 - is_read (yes/no, default: no)
17 - Created Date (auto timestamp)
18
19CONVERSATION LIST:
20 Repeating Group: Conversation
21 Source: Search Conversations
22 where participants contains Current User
23 sorted by last_message_time desc
24 Cell shows: other user name, last_message,
25 last_message_time, unread count badge
26
27MESSAGE THREAD:
28 Repeating Group: Message
29 Source: Search Messages
30 where conversation = selected conversation
31 sorted by Created Date asc
32 Cell alignment:
33 sender = Current User right, blue
34 sender Current User left, gray
35
36SEND MESSAGE WORKFLOW:
37 When Send clicked (or Enter pressed):
38 1. Create new Message
39 body = Input's value
40 sender = Current User
41 conversation = selected conversation
42 2. Make changes to Conversation
43 last_message = Input's value
44 last_message_time = Current date/time
45 3. Reset Input
46 4. Scroll RG to last entry
47
48MARK AS READ:
49 When conversation selected:
50 Make changes to list of Messages
51 where conversation = selected
52 and is_read = no
53 and sender Current User
54 Set is_read = yes
55
56TYPING INDICATOR:
57 Input value changed Add Current User to
58 Conversation's typing_users
59 Schedule custom event (3 sec delay) Remove
60 Current User from typing_users
61 Display: 'X is typing...' when typing_users
62 is not empty and only Current User

Common mistakes when building a Chat System in Bubble

Why it's a problem: Searching for messages inside each conversation list cell to get the last message

How to avoid: Store last_message and last_message_time directly on the Conversation record and update them when a new message is sent

Why it's a problem: Not sorting messages by Created Date ascending

How to avoid: Set the message Repeating Group sort to Created Date ascending so oldest messages are at the top and newest at the bottom

Why it's a problem: Forgetting to reset the message input after sending

How to avoid: Add a reset relevant inputs or set input value to empty as the last step in the send workflow

Best practices

  • Store conversation metadata like last message on the Conversation record for efficient list display
  • Use Bubble's auto-updating searches for real-time message delivery without extra plugins
  • Paginate message history to load only the most recent 50 messages initially
  • Mark messages as read when the conversation is opened, not when individual messages are displayed
  • Use conditional alignment to visually distinguish sent and received messages
  • Add timestamps to message groups rather than individual messages for cleaner UI
  • Test with multiple browser windows to verify real-time sync between users

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I want to build a real-time one-on-one chat system in my Bubble.io app with message history, unread badges, and typing indicators. What data model should I use and how do I set up real-time updates?

Bubble Prompt

Help me build a messaging feature for my app. I need a conversation list on the left showing recent chats with unread counts, and a message thread on the right that updates in real time.

Frequently asked questions

Does Bubble support real-time chat without plugins?

Yes. Bubble's database searches on page elements auto-update via WebSocket. When a new message is created, it appears automatically in the other user's message list without polling or plugins.

How many messages can a conversation handle?

There is no hard limit, but loading thousands of messages at once will be slow. Paginate the message list to show the most recent 50-100 messages and load more on scroll.

Can I build group chat with this approach?

Yes. The participants field is a list of Users, supporting any number of participants. For group chat, display the conversation name and show sender names on each message.

How do I handle message notifications?

Use a database trigger on Message creation to send push notifications or emails to other conversation participants who are not currently viewing the conversation.

Will the chat system work on mobile?

Yes. Make the layout responsive by hiding the sidebar on mobile and showing a full-screen conversation list that navigates to the message thread.

Can RapidDev help build a chat system for my app?

Yes. RapidDev can build complete messaging systems including group chat, file sharing, typing indicators, and push notifications for your Bubble application.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.