Searches, filters, counts, and repeating groups
Put constraints in the search first
If Bubble can narrow the list in the database, do that before the page receives the data.
For example, this is the right shape for a marketplace repeating group:
Do a search for Products
Category = Dropdown Category's value
Status = Available
Price >= Input Min Price's value
Price <= Input Max Price's valueThe worse shape is:
Do a search for Products:filteredwith the category, status, and price logic hidden in an advanced filter or several workflow steps.
:filtered is fine when the list is small or already loaded. It is a bad default for large product lists, dashboards, CRMs, admin tables, and anything users will keep adding records to.
The reason is simple: a database constraint changes what Bubble asks the database for. A client-side filter works after Bubble already has the data, or after Bubble decides it needs to fetch more data to apply the filter.
Bubble does have some smart behaviour here. It can reuse data already on the page, and some filter constraints can run locally. Do not rely on that as your main performance plan. Start with a constrained Do a search for....
Know which filters can run locally
Some filters can be applied to data Bubble already has in memory. Basic comparisons like equals, greater than, less than, is empty, and contains can often be handled locally.
Some constraints need a database search, or at least cannot be treated like a simple in-memory filter. Examples from the forum research included geographic filters, text keyword searches, Any field, range checks, and email-field comparisons.
You do not need to memorise every edge case. The useful rule is:
Use normal search constraints for the filters that define the result set.
Use :filtered for small local adjustments.For example, if the page is for Invoices, constrain the search by Company, Status, and Due Date. If the user is toggling a tiny already-loaded list between "show totals" and "show overdue only", :filtered is less worrying.
Repeating groups are usually slow because of their data source
When a repeating group is slow, check the data source before blaming the repeating group element.
The main questions are:
How many things does the search return?
How large is each thing?
Is each cell doing another search?
For example, a repeating group of Listings will be heavier if every Listing has long descriptions, large list fields, and related things that the cell immediately loads. Showing 200 of those at once is going to hurt.
Fix the obvious parts first:
add database constraints;
paginate or use vertical scrolling carefully;
do not show more cells than the user can use;
avoid searches inside each cell;
avoid loading long text fields or large list fields when the card only needs title, price, and status.
Sometimes a satellite data type is worth it. For example, a Listing Search Result type might store the fields needed for the search card, while the full Listing stores the long description and admin-only fields.
I would not add that sync work by default. It is worth considering when the main data type is unusually heavy and searched a lot.
Do not rerun broad searches on every input change
If a user is changing five filters, the app does not need to rerun the full repeating group search five times.
One useful pattern is:
Filter inputs change
-> trigger custom event
-> display list in repeating groupor just use a search button if that is acceptable for the product.
This gives you one place to inspect the search. It also stops the repeating group's data source from constantly recalculating while the user is still choosing filters.
For dynamic sorting, use Bubble's "Change which field" option in Sort by where it fits. An option set like Created date asc, Created date desc, Price asc, Price desc is usually easier to maintain than duplicating search expressions in conditionals.
Avoid daisy-chain filtering
Do not split one search into a chain of filters unless there is a specific reason.
This is the shape I would avoid:
Step 1: get all products
Step 2: filter by category
Step 3: filter by price
Step 4: filter by availability
Step 5: display the listIf those constraints can live in the original Do a search for Products, put them there.
The daisy-chain version is harder to debug because the list changes in several places. It is also more likely to fetch too much data before narrowing it.
There are exceptions. Sometimes you need to filter against a calculated list that Bubble cannot express in a normal search constraint. If so, make the first search narrow enough that the later filter is not working on a huge list.
Use counts as counts
If you only need a count, use :count. Do not load the list into a hidden group or repeating group just to count it.
For example:
Do a search for Orders
Customer = Current User
Status = Open
:countThat is the right shape for an open-order badge or an empty-state check.
Avoid this kind of thing:
Hidden group = Do a search for Orders
Text = Hidden group's Orders:countYou have loaded a list when the page only needed a number.
One thing to flag: older forum testing found that Do a search for Things:count is 0 behaved differently from some positive-count checks. I would retest that before making a current claim about Bubble's runtime. The safer guidance is to check the exact expression in a high-traffic page instead of assuming every count expression has the same workload behaviour.
Fields used for privacy rules can still be returned
A field does not become free because you only use it in a privacy rule.
For example:
Project
Collaborators (list of Users)You might never display Project's Collaborators. It may only exist so the privacy rule can say "Current User is in Project's Collaborators".
That can be fine. But if Bubble returns the Project, that list field may still be part of the data being returned. It is only a list of IDs, not the full user objects, so do not overreact. Just do not put huge access-control lists on heavily searched things without thinking about it.
If a Company can have thousands of users, consider a Company Membership data type instead of one huge list field on Company. That gives you a thing you can search, constrain, and attach role metadata to.
What to check when a list is slow
Start with the repeating group data source.
Check:
Is the
Do a search for...constrained by the fields users actually filter by?Is
:filteredbeing used on a large list?Does the page load more rows than the user can see?
Does each cell run another search?
Are you loading a list when you only need
:count?Are large list fields being returned with every thing?
Could a search button or
Display listcustom event stop repeated searches while filters are changing?
Fix those before spending time on small expression cleanups.