# How to Build Responsive Data Tables in Bubble

- Tool: Bubble
- Difficulty: Beginner
- Time required: 20-25 min
- Compatibility: All Bubble plans
- Last updated: March 2026

## TL;DR

Bubble does not have a native table element, but you can build responsive data tables using Repeating Groups styled as rows with a fixed header Group above. This tutorial covers creating the table layout with column headers, adding sort functionality by clicking headers, enabling horizontal scroll for mobile, and implementing inline editing so users can update records directly in the table.

## Overview: Responsive Data Tables in Bubble

This tutorial teaches you how to display database records in a professional table format. You will create a structured layout with column headers and data rows, add sorting and pagination, and make it mobile-friendly with horizontal scroll. The approach uses Bubble's native Repeating Group element.

## Before you start

- A Bubble app with a Data Type containing records to display
- Basic understanding of Repeating Groups and data sources
- Familiarity with Bubble's responsive layout system
- Understanding of custom states for interactivity

## Step-by-step guide

### 1. Create the table header row

In the Design tab, add a Group element set to Row layout. This will be your header row. Inside it, add Text elements for each column header — for example, Name, Email, Date Joined, and Status. Set each text element to a fixed width that matches the column widths you want. Style the header Group with a background color to distinguish it from data rows. Make each header text bold. Set the Group's width to a fixed value wider than the page if you need more columns — this enables horizontal scrolling.

**Expected result:** A styled header row displays column labels above where the data rows will appear.

### 2. Build the data rows with a Repeating Group

Below the header, add a Repeating Group with its type set to your Data Type. Set the layout to Row in a column with a fixed number of rows (10-20 for pagination). Inside the first cell, create a Row group matching the header layout. Add Text elements for each column, binding them to the current cell's data fields. Match each column's width exactly to the corresponding header column. Add alternating row colors using a conditional on the Repeating Group cell: When current cell's index is odd → background color is slightly different.

**Expected result:** Data records display in aligned rows beneath the header, with each column's data matching its header label.

### 3. Add sortable column headers

Create two custom states on the page: 'sort_field' (type text) and 'sort_direction' (type text, default 'ascending'). Make each header text clickable by adding a workflow to each one. When a header is clicked, set sort_field to that column name and toggle sort_direction between ascending and descending. Update the Repeating Group's data source to use Dynamic sorting — set Sort by to a dynamic value based on sort_field and Sort order based on sort_direction. Add a small arrow icon next to each header that changes direction based on the current sort state.

> Pro tip: Bubble's Do a Search For supports :sorted operator which lets you sort by any field dynamically. Combine this with your custom state for the sort field.

**Expected result:** Clicking a column header sorts the table by that column, and clicking again reverses the sort order.

### 4. Enable horizontal scroll for mobile

Wrap both the header Group and the Repeating Group inside a parent Group. Set this parent Group's width to match the total table width. Then wrap that Group inside another outer Group that has a fixed width matching the page and 'Allow horizontal scrolling when content overflows' enabled. On mobile breakpoints, the outer container stays within screen bounds while the inner table extends beyond it, allowing users to scroll horizontally to see all columns. Test this by resizing your browser to mobile width.

**Expected result:** On mobile screens, the table scrolls horizontally to reveal all columns while the page layout stays intact.

### 5. Implement inline cell editing

For editable columns, replace the Text element in the Repeating Group cell with an Input element. Set the Input's initial content to the current cell's field value. Set the Input to appear as plain text by default using conditional formatting — remove borders and background when it is not focused. Add a workflow: When the Input's value is changed and the Input is not focused → Make changes to Current cell's data type, setting the field to the Input's value. This auto-saves changes when the user clicks away from the cell.

> Pro tip: Add visual feedback when a cell is being edited — show a subtle border or highlight color when the Input is focused so users know they are in edit mode.

**Expected result:** Users can click on a table cell, edit the value inline, and the change saves automatically when they click away.

## Complete code example

File: `Workflow summary`

```text
RESPONSIVE TABLE SETUP SUMMARY
=====================================

HEADER ROW:
  Group (Row layout, background color)
    → Text: Name (width: 200px, bold)
    → Text: Email (width: 250px, bold)
    → Text: Date Joined (width: 150px, bold)
    → Text: Status (width: 120px, bold)

DATA ROWS:
  Repeating Group (type: [Data Type])
    Layout: Column, fixed rows: 15
    Data source: Do a Search sorted by sort_field
    Cell → Group (Row layout)
      → Text/Input: Name (200px)
      → Text/Input: Email (250px)
      → Text: Date (150px, formatted)
      → Text: Status (120px)
    Conditional: odd index → alt background

SORTING:
  Custom states on page:
    sort_field (text, default: 'Created Date')
    sort_direction (text, default: 'descending')
  Header click workflow:
    Set sort_field = column name
    Toggle sort_direction
  RG data source: sorted by sort_field
  Arrow icon: conditional on sort_direction

HORIZONTAL SCROLL:
  Outer Group: page width, overflow scroll
    Inner Group: total table width (720px+)
      Header Group
      Repeating Group

INLINE EDITING:
  Replace Text with Input in editable cells
  Input initial content = current cell's field
  Conditional: not focused → no border
  Workflow: value changed + not focused
    → Make changes to current cell's thing
    → Set field = Input's value

PAGINATION:
  Custom state: current_page (number)
  Next/Previous buttons
  RG offset = (current_page - 1) * rows_per_page
```

## Common mistakes

- **Not matching column widths between the header and data rows** — Mismatched widths make columns appear misaligned, destroying the table appearance Fix: Set explicit fixed widths on both header texts and corresponding data cell elements, ensuring they match exactly
- **Using a full-width table on mobile without horizontal scroll** — Wide tables overflow the screen and break the layout or become unreadable Fix: Wrap the table in a container with horizontal scroll enabled so mobile users can swipe to see all columns
- **Running searches inside repeating group cells for computed columns** — A search per cell multiplies queries by the number of visible rows, causing severe slowdowns Fix: Pre-compute values as fields on the Data Type or use parent group references

## Best practices

- Use fixed column widths to maintain alignment between headers and data rows
- Paginate to 10-20 rows for optimal performance and usability
- Add alternating row colors for better readability
- Implement horizontal scroll for tables wider than mobile screens
- Show sort direction indicators on column headers for clear UX
- Use inline editing only for fields users frequently update
- Add a loading indicator while data is being fetched

## Frequently asked questions

### Does Bubble have a built-in table element?

No. Bubble does not have a native table element. Tables are built using Repeating Groups styled as rows with a header Group above for column labels.

### How many columns can a Bubble table have?

There is no hard limit on columns. However, more than 8-10 columns may require horizontal scrolling on desktop and becomes less user-friendly. Consider which columns are essential for your use case.

### Can I export the table data to CSV?

Yes. Add an export button that uses a CSV plugin to generate a downloadable file from the same data source as your Repeating Group.

### How do I add a search or filter above the table?

Add an Input element above the table and use its value as a constraint in the Repeating Group's data source. Filter by text contains on the relevant field.

### Is inline editing secure?

Inline editing triggers Make changes to a thing, which is subject to Privacy Rules. Ensure your Privacy Rules allow editing only for authorized users.

### Can RapidDev help build complex data tables?

Yes. RapidDev can create advanced data table interfaces with multi-column sorting, advanced filtering, inline editing, and bulk actions for your Bubble application.

---

Source: https://www.rapidevelopers.com/bubble-tutorial/build-responsive-tables-in-bubble
© RapidDev — https://www.rapidevelopers.com/bubble-tutorial/build-responsive-tables-in-bubble
