Display database records in a table format in Bubble using Repeating Groups configured as rows and columns. This tutorial covers building a responsive data table with sortable columns, pagination, inline editing, and search filtering so you can present structured data clearly to your users or admin team.
Overview: Using Data Tables in Bubble
This tutorial shows you how to display database records in a clean table format using Bubble's Repeating Group element. You will build a table with header rows, sortable columns, row pagination, and inline cell editing. This is essential for admin panels, dashboards, and any page that needs to show structured data in rows and columns.
Prerequisites
- A Bubble account with a new or existing app
- A Data Type with records to display (this tutorial uses a Product Data Type as an example)
- Basic familiarity with Repeating Groups in Bubble
Step-by-step guide
Create the table header row
Create the table header row
On your page in the Design tab, add a Row container group at the top of where you want the table. Inside it, add Text elements for each column header: Name, Price, Category, Status, and Actions. Set each text to bold and give the row a light background color like #F3F4F6 to distinguish it from data rows. Set consistent widths for each column by giving each text element a fixed or percentage width.
Expected result: A styled header row with column labels appears at the top of your table area.
Build the data rows with a Repeating Group
Build the data rows with a Repeating Group
Below the header row, add a Repeating Group with Type of content set to your Data Type (such as Product). Set the Data source to Do a Search for Products sorted by Created Date descending. Set the layout to Column with each row being a fixed height of about 48 pixels. Inside each cell, add a Row container. Inside that row, add Text elements matching each header column: Current cell's Product's Name, Current cell's Product's Price formatted as currency, Current cell's Product's Category, and Current cell's Product's Status. Match the widths to the header columns.
Pro tip: Add alternating row colors using a conditional: when Current cell's index is odd, set the background to white. When even, set it to a very light grey like #F9FAFB.
Expected result: A table of data rows appears below the header, with each record displayed in aligned columns matching the header.
Add sortable column headers
Add sortable column headers
Create a custom state on the page called Sort Field (text, default: Created Date) and another called Sort Order (text, default: descending). On each header Text element, add a click workflow: Set state of page, Sort Field equals the field name (such as Name or Price). In the same workflow, toggle Sort Order between ascending and descending. Update the Repeating Group's data source to use Dynamic sort: Sort Field with direction Sort Order. Add a small arrow icon next to each header that changes direction based on the current sort state.
Expected result: Clicking a column header sorts the table by that column. Clicking again reverses the sort direction.
Implement pagination controls
Implement pagination controls
Set the Repeating Group's number of rows to show to a fixed number like 10 or 20. Below the Repeating Group, add a Row container with pagination controls. Add a Text element showing the current page range (such as Showing 1-10 of 50). Add Previous and Next buttons. For the Next button, create a workflow that increments a custom state called Page Number. Use the Repeating Group's Show next and Show previous actions to navigate between pages. Add an Only when condition to disable Previous on page 1 and Next on the last page.
Expected result: The table shows a fixed number of rows per page with Previous and Next buttons to navigate through all records.
Enable inline editing of table cells
Enable inline editing of table cells
To let users edit data directly in the table, replace one of the Text elements (such as Status) with an Input element. Set its initial content to Current cell's Product's Status. Add a workflow: When Input's value is changed, Make changes to a thing, set Current cell's Product's Status to this Input's value. For a cleaner look, style the Input to look like a Text element (no border, transparent background) and only show a border on focus using the Conditional tab.
Pro tip: For dropdown selections like Status, use a Dropdown element instead of an Input. Populate it from an Option Set for consistent values.
Expected result: Users can click a cell, edit the value, and the change is saved to the database when they click away or press Enter.
Add a search filter above the table
Add a search filter above the table
Above the header row, add a Search Input element. Modify the Repeating Group's data source to include a constraint: Name contains Search Input's value, with Ignore empty constraints checked. This filters the table as the user types. For more advanced filtering, add Dropdown filters for Category and Status with their own constraints on the same search.
Expected result: Typing in the search bar filters the table rows in real time. Additional dropdown filters narrow results by category or status.
Complete working example
1DATA TABLE — WORKFLOW SUMMARY2==============================34PAGE LAYOUT:5 Search Input + Filter Dropdowns6 Header Row (Row container, bg #F3F4F6):7 Text: Name | Price | Category | Status | Actions8 Repeating Group (Type: Product, Rows: 10)9 Source: Search Products10 Constraints: Name contains Search value11 Sort: [Sort Field] custom state, [Sort Order] custom state12 Ignore empty constraints: checked13 Pagination Row:14 Text: 'Showing X-Y of Z'15 Button: Previous → RG Show previous16 Button: Next → RG Show next1718CUSTOM STATES (on page):19 - Sort Field (text, default: 'Created Date')20 - Sort Order (text, default: 'descending')21 - Page Number (number, default: 1)2223WORKFLOW 1: Sort Column24 Trigger: Header text clicked25 Actions:26 1. Set state Sort Field = clicked column's field name27 2. Set state Sort Order = toggle asc/desc2829WORKFLOW 2: Inline Edit30 Trigger: Input value changed31 Actions:32 1. Make changes to thing → Current cell's Product33 [field] = Input's value3435WORKFLOW 3: Next Page36 Trigger: Button Next clicked37 Condition: Page Number < total pages38 Actions:39 1. RG Show next40 2. Set state Page Number + 14142WORKFLOW 4: Previous Page43 Trigger: Button Previous clicked44 Condition: Page Number > 145 Actions:46 1. RG Show previous47 2. Set state Page Number - 14849STYLING:50 Alternating rows: Conditional on cell index51 Odd → bg white, Even → bg #F9FAFB52 Inline inputs: no border, transparent bg53 On focus → show borderCommon mistakes when using DataTables plugins in Bubble
Why it's a problem: Not matching header column widths to data row widths
How to avoid: Use fixed pixel or percentage widths on both header texts and data row elements, ensuring they match exactly
Why it's a problem: Loading all records without pagination
How to avoid: Set the Repeating Group to show 10-20 rows and add pagination controls for navigating through the dataset
Why it's a problem: Using client-side filtered operator instead of search constraints for filtering
How to avoid: Use search constraints (server-side filtering) with the Ignore empty constraints checkbox for efficient data retrieval
Best practices
- Use fixed column widths on both headers and data cells to maintain alignment
- Paginate large datasets to 10-20 rows per page for performance
- Use search constraints instead of the filtered operator for server-side filtering
- Add alternating row colors for better readability
- Style inline edit inputs to look like text until focused for a clean appearance
- Include a record count display so users know how many total records exist
- Add Privacy Rules to ensure users only see records they are authorized to view
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I want to build a data table in Bubble.io that displays my Product records with sortable columns, pagination, inline editing, and a search filter. How should I structure the Repeating Group and workflows?
Create a data table showing Product records with columns for Name, Price, Category, and Status. Add sortable headers, pagination with 10 rows per page, and a search filter above the table.
Frequently asked questions
Can I export the table data to CSV?
Bubble does not have a native table export feature. You can go to Data tab then App data to export via the built-in CSV export, or use a plugin like CSV Creator to let users export directly from the table.
How do I handle tables with many columns on mobile?
Hide less important columns on smaller screens using Bubble's responsive breakpoints and conditional visibility. Alternatively, switch to a card layout on mobile where each record is a vertical card instead of a horizontal row.
Can I add a delete button to each row?
Yes. Add a button or icon in the Actions column of each row. Create a workflow: When delete button is clicked, Delete a thing, thing is Current cell's Product. Add a confirmation popup before deletion to prevent accidents.
Is there a table plugin for Bubble?
Yes, there are several table plugins on the Bubble marketplace including Table Grid and Data Grid plugins that offer features like column resizing, row selection, and built-in sorting. However, building with Repeating Groups gives you full control over styling and behavior.
Can RapidDev help build complex admin dashboards?
Yes. RapidDev can help you build sophisticated admin panels with advanced data tables, bulk operations, role-based access, and integrated analytics dashboards.
How do I sort by multiple columns at once?
Bubble's native sort supports one primary sort field. For multi-column sorting, sort server-side using the primary column in the search constraint, then use the secondary sort as the Sort by parameter. Complex multi-sort may require a Backend Workflow that pre-sorts and caches results.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation