A custom search bar in Bubble connects a text Input element to a Repeating Group using Do a Search For with dynamic constraints. As the user types, the Repeating Group filters results in real time. This tutorial covers the complete setup including multi-field search, debouncing, and empty state handling.
Overview: Building a Custom Search Bar in Bubble
Search is one of the most-used features in any app. Bubble lets you build a real-time search bar by connecting a text Input to a Repeating Group's data source with dynamic constraints. This tutorial walks you through building a search bar that filters results as the user types, searches across multiple fields, and handles edge cases gracefully.
Prerequisites
- A Bubble app with a Data Type that has records to search through
- A page with a Repeating Group displaying those records
- Basic understanding of Bubble elements and data sources
Step-by-step guide
Add a search Input element above your Repeating Group
Add a search Input element above your Repeating Group
In the Design tab, add an Input element above your Repeating Group. Set its placeholder text to something helpful like Search by name, email, or keyword. Leave the content format as Text. Optionally add a search icon using an Icon element positioned inside the input's group for visual clarity.
Expected result: A text input field appears above the list, ready to accept search queries.
Connect the search input to the Repeating Group data source
Connect the search input to the Repeating Group data source
Click on your Repeating Group and open its Data source. Set it to Do a Search for your Data Type. Add a constraint: the field you want to search (e.g., title) contains SearchInput's value. This filters the results to only show records where the title contains the user's search text. The search updates automatically as the user types because Bubble re-evaluates the data source whenever the input value changes.
Pro tip: Use the contains operator for partial matching. The equals operator requires an exact match, which is rarely what users want.
Expected result: The Repeating Group filters in real time as the user types in the search input.
Search across multiple fields
Search across multiple fields
To search across multiple fields (e.g., title, description, category), you need a workaround since Bubble constraints are AND-based. Option 1: create a search_text field on your Data Type that concatenates all searchable fields (set via a workflow when records are created or modified). Search against this single field. Option 2: use the :merged with operator to combine two separate searches (one for title, one for description) into a single list for the Repeating Group.
Pro tip: The concatenated search_text field approach is more performant because it requires only one database search instead of merging multiple searches.
Expected result: The search bar finds matches across multiple fields, not just one.
Handle empty states and no-results messages
Handle empty states and no-results messages
Add a Group below the Repeating Group containing a Text element that says No results found. Try a different search term. Add a conditional on this Group: visible when RepeatingGroup's list of Data Type's count is 0 AND SearchInput's value is not empty. Also add a conditional on the Repeating Group itself: collapse its height when hidden to prevent empty space. This gives users clear feedback when their search returns no results.
Expected result: A helpful no-results message appears when the search finds nothing, and disappears when results exist.
Add the Ignore empty constraints option
Add the Ignore empty constraints option
In your Repeating Group's Do a Search for expression, check the Ignore empty constraints checkbox. This ensures that when the search input is empty (the user has not typed anything), all records are shown instead of no records. Without this, an empty input would match nothing because Bubble looks for records containing an empty string.
Expected result: All records display when the search input is empty, and filtered results display when the user types.
Complete working example
1CUSTOM SEARCH BAR SUMMARY2==========================34ELEMENTS:5 Input: SearchInput6 - Placeholder: "Search by name or keyword..."7 - Content format: Text89 RepeatingGroup: ResultsList10 - Type of content: Product (your Data Type)11 - Data source: Do a Search for Products12 Constraint: title contains SearchInput's value13 ☑ Ignore empty constraints14 - Layout: column, rows per page: 101516 Group: NoResultsGroup17 - Visible on page load: no18 - Contains Text: "No results found. Try a different search."19 - Conditional: When ResultsList's list count is 020 AND SearchInput's value is not empty21 → This element is visible: yes2223MULTI-FIELD SEARCH (Option A — concatenated field):24 Data Type: Product25 - title (text)26 - description (text)27 - search_text (text) = title + " " + description + " " + category28 29 When Product is created:30 → search_text = title + " " + description + " " + category3132 RG source: Search for Products33 Constraint: search_text contains SearchInput's value34 ☑ Ignore empty constraints3536MULTI-FIELD SEARCH (Option B — merged searches):37 RG source:38 Search for Products (title contains SearchInput's value)39 :merged with40 Search for Products (description contains SearchInput's value)41 :unique elements4243PERFORMANCE NOTES:44 - contains matches first 256 characters only45 - Use constraints, not :filtered, for search46 - Paginate to 10-20 items per page47 - Option A (search_text) is faster than Option B (merged)Common mistakes when creating a custom search bar in Bubble.io: Step-by-Step Guide
Why it's a problem: Not checking Ignore empty constraints
How to avoid: Always check the Ignore empty constraints option on the Do a Search for expression
Why it's a problem: Using :filtered instead of search constraints
How to avoid: Use Do a Search For constraints for filtering — they are processed server-side and use database indexes
Why it's a problem: Searching on unindexed fields in large databases
How to avoid: Create a dedicated search_text field that concatenates searchable data, and keep it under 256 characters if possible
Best practices
- Always check Ignore empty constraints to show all records when the search is empty
- Use Do a Search For constraints instead of the :filtered operator for better performance
- Create a concatenated search_text field for multi-field search on a single constraint
- Show a no-results message when the search returns empty
- Paginate results to 10-20 items per page to keep the Repeating Group fast
- Add a clear button (X icon) inside the search input to let users reset their search easily
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I need to build a search bar in my Bubble.io app that filters a Repeating Group of products in real time as the user types. I want to search across the product title and description fields. What is the best approach for performance?
Add a search bar to my products page. It should filter the Repeating Group in real time as I type, searching across the title and description fields. Show a no-results message when nothing matches.
Frequently asked questions
Does the search update in real time as I type?
Yes. When a Repeating Group's data source references an Input's value, Bubble automatically re-evaluates the search whenever the input changes. There is a slight delay as Bubble processes each keystroke.
Is Bubble search case-sensitive?
No. Bubble's contains operator is case-insensitive by default, so searching for apple will match Apple, APPLE, and apple.
How do I improve search performance on large datasets?
Use search constraints instead of :filtered, create a concatenated search_text field, paginate results, and for very large datasets (10,000+ records) consider an external search service like Algolia via the API Connector. RapidDev can help integrate advanced search for high-performance apps.
Can I add filters alongside the search (e.g., category dropdown)?
Yes. Add more constraints to the Do a Search For expression. For example, add category = CategoryDropdown's value as a second constraint alongside the text search.
Why does my search only match the beginning of text?
The contains operator matches any position in the text. If you are only getting prefix matches, you might be using the starts with operator instead. Check your constraint setup.
What is the 256-character limitation for text search?
Bubble indexes only the first 256 characters of text fields for search purposes. If the text you want to match is beyond the 256th character, the contains constraint will not find it. Keep searchable content near the beginning of the field.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation