Bullet-Proof Pagination in Bubble's New Native Engine

:mobile_phone: The Pagination Playbook for Bubble’s Native Engine

(a.k.a. how to stop blasting 10 000 rows into your user’s phone)

Hey everyone! — Adam here. When I first ported my web app to Bubble’s shiny React-Native stack I hit the same wall again and again:

“Short List is okay… until the database crosses five figures — then the app turns into sludge. And the workload units shoot through the roof.”

Below is the recipe that finally gave me silky-smooth scrolling whether I’m showing 8 items or 80 000.
Two flavours, the same Prev / Next buttons. Copy, paste, tweak, done. Ready?


:shortcake: Flavour #1 — when the list is actually short

If you’ll never show more than ±30 rows, life is easy:

Piece Why it exists
current_page (number, default = 1) Tracks where we are
page_size (5 – 10) Rows per page
Short List Renders that slice
:reverse_button: Prev / Next :play_button: Flip the slice

Data source (Bubble expression)

Search for Things
    :sorted by Name (DESC=yes)
    :items from (((current_page - 1) * page_size) + 1)
    :items until page_size

Workflows

Trigger Action
Next current_page = current_page + 1
Prev current_page = current_page - 1 (disable when 1)

That’s literally it. The Short List still loads every row, but who cares — there are only a few dozen. :white_check_mark:

:t_rex: Flavour #2 — when the list is a dinosaur (100+ rows)

1 · Expose the Data API (one-time)

  1. Settings → API → :check_mark: Enable Data API → tick your Thing.
  2. Grab an admin token (or use Private key in header).

Your endpoint now looks like:

GET https://your-app.bubbleapps.io/api/1.1/obj/product

2 Build a paged call in API Connector

Field Value
Name Products – paged
Method GET
URL https://your-app.bubbleapps.io/api/1.1/obj/product?limit=[page_size]&cursor=[offset]&constraints=[constraints]
Headers Authorization: Bearer <TOKEN>
Use as Data
Key path results

Parameters:

  • page_size (number, default = 5)
  • offset (number, default = 0)
  • constraints (text, default = [{ “key”: “name”, “constraint_type”: “text contains”, “value”: “Product 1” }])

3 Same UI, new data source

Bind your Short / Vertical List to Products – paged and map:

page_size = 10
offset     = (current_page - 1) * page_size
constraints = [{     "key": "name",     "constraint_type": "text contains",     "value": "Input Name's value"    }]

Buttons & states stay exactly the same.

4 But how many pages do I have?

count = Product API - paged's response remaining + page_size

So, on Page load:

total_pages = (count / page_size):ceiling

Then you can sshow a label like “Page X of total_pages”.

:magnifying_glass_tilted_right: Debug / UX checklist

  • Always :sorted by something stable before paginating.
  • Disable Prev on page 1, disable Next on the last page.
  • Show a tiny loader while the API call is :counterclockwise_arrows_button:.
  • Lock down your Privacy Rules — the Data API respects them.

:puzzle_piece: Starter pack (copy ➜ paste)

  1. Group named controller
  • states: current_page = 1, page_size = 5, total_pages
  1. Short List wired to Flavour #1 or Flavour #2
  2. Two buttons → current_page ± 1
  3. On page load → fetch count → set total_pages

One reusable, zero-lag component that scales from 5 → 50 000 rows just by swapping the data source. :tada:


Hope I could help you make safe and scalable native apps!

7 Likes

Bro this is gold! Can you do a video tutorial and post it on youtube showing the After/Before behavior examples and show exactly what you said here, but in a video?

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.