# How to create a custom search bar in Bubble.io: Step-by-Step Guide

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

## TL;DR

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.

## Before you start

- 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

### 1. 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.

### 2. 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.

### 3. 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.

### 4. 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.

### 5. 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 code example

File: `Workflow summary`

```text
CUSTOM SEARCH BAR SUMMARY
==========================

ELEMENTS:
  Input: SearchInput
    - Placeholder: "Search by name or keyword..."
    - Content format: Text

  RepeatingGroup: ResultsList
    - Type of content: Product (your Data Type)
    - Data source: Do a Search for Products
      Constraint: title contains SearchInput's value
      ☑ Ignore empty constraints
    - Layout: column, rows per page: 10

  Group: NoResultsGroup
    - Visible on page load: no
    - Contains Text: "No results found. Try a different search."
    - Conditional: When ResultsList's list count is 0
      AND SearchInput's value is not empty
      → This element is visible: yes

MULTI-FIELD SEARCH (Option A — concatenated field):
  Data Type: Product
    - title (text)
    - description (text)
    - search_text (text) = title + " " + description + " " + category
  
  When Product is created:
    → search_text = title + " " + description + " " + category

  RG source: Search for Products
    Constraint: search_text contains SearchInput's value
    ☑ Ignore empty constraints

MULTI-FIELD SEARCH (Option B — merged searches):
  RG source:
    Search for Products (title contains SearchInput's value)
    :merged with
    Search for Products (description contains SearchInput's value)
    :unique elements

PERFORMANCE NOTES:
  - contains matches first 256 characters only
  - Use constraints, not :filtered, for search
  - Paginate to 10-20 items per page
  - Option A (search_text) is faster than Option B (merged)
```

## Common mistakes

- **Not checking Ignore empty constraints** — When the search input is empty, Bubble searches for records containing an empty string, which returns nothing Fix: Always check the Ignore empty constraints option on the Do a Search for expression
- **Using :filtered instead of search constraints** — The :filtered operator processes each record individually after fetching all records, which is very slow for large datasets Fix: Use Do a Search For constraints for filtering — they are processed server-side and use database indexes
- **Searching on unindexed fields in large databases** — Text search with the contains operator on fields with 1000+ records can be slow because Bubble only indexes the first 256 characters Fix: 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

## 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.

---

Source: https://www.rapidevelopers.com/bubble-tutorial/create-a-custom-search-bar-in-bubble
© RapidDev — https://www.rapidevelopers.com/bubble-tutorial/create-a-custom-search-bar-in-bubble
