An e-commerce filter system in Bubble uses custom states to track user selections and combines them into a single search constraint on a product Repeating Group. This tutorial covers building checkbox category filters with product counts, a price range filter with min and max inputs, visual color and size selectors, combining all active filters into one efficient database query using Ignore empty constraints, and displaying removable filter tags above the results grid.
Overview: Building an E-Commerce Filter System in Bubble
Product filtering is essential for any e-commerce app with more than a handful of items. This tutorial builds a professional multi-facet filter sidebar that lets shoppers narrow products by category, price range, color, and size -- all updating a single Repeating Group in real time. It is designed for non-technical founders building product catalogs, marketplaces, or directory apps in Bubble.
Prerequisites
- A Bubble app with a Product Data Type containing fields for name, price, category, color, and size
- At least 10-20 sample products in the database for testing filters
- Basic understanding of custom states and how they store temporary values
- Familiarity with Repeating Groups and search constraints
Step-by-step guide
Set up the page layout and custom states for filters
Set up the page layout and custom states for filters
Create a new page called 'shop'. Add a Group element on the left side (250-300px wide) as the filter sidebar. Add a larger Group on the right for the product grid. On the page itself, create these custom states: selected_categories (type: list of texts), min_price (type: number), max_price (type: number), selected_colors (type: list of texts), selected_sizes (type: list of texts). These states will hold the user's current filter selections and drive the product search. At the top of the sidebar, add a 'Clear All Filters' button. Its workflow will reset every custom state to empty. Inside the sidebar, create four collapsible Group sections with headings: Categories, Price Range, Colors, and Sizes.
Expected result: A two-column page layout with a filter sidebar containing four sections and five custom states ready to track user selections.
Build the checkbox category filters with product counts
Build the checkbox category filters with product counts
Create an Option Set called ProductCategory with options matching your catalog (e.g., Electronics, Clothing, Home, Sports). Inside the Categories section, add a Repeating Group with type ProductCategory and data source: All ProductCategories. In each cell, place a Checkbox element and a Text element showing the option's Display value. Next to it, add another Text element displaying the count: Do a search for Product where category = Current cell's ProductCategory :count. This shows users how many products each category contains. Add a workflow on the Checkbox element: When Checkbox value changes and is checked, set state selected_categories to selected_categories plus item Current cell's ProductCategory's Display. When unchecked, set state selected_categories to selected_categories minus item Current cell's ProductCategory's Display.
Pro tip: Use an Option Set for categories instead of a Data Type. Option Sets load instantly without a database query, making your filter sidebar snappy even with many categories.
Expected result: Checking or unchecking a category immediately updates the selected_categories custom state, and each category shows a product count.
Add the price range filter
Add the price range filter
Inside the Price Range section, add two Input elements side by side: one labeled 'Min' and one labeled 'Max'. Set both content formats to Integer. Below the inputs, add a Text element showing the current range: 'Showing products from $Min to $Max'. Add two workflows. Workflow 1: When Min input's value changes, set state min_price to Min input's value. Workflow 2: When Max input's value changes, set state max_price to Max input's value. If you want a more visual control, install a Range Slider plugin from the Bubble Plugin Marketplace and configure it with min = 0, max = your highest product price, and two handles. Map each handle to the corresponding custom state.
Expected result: Entering or adjusting min and max price values updates the custom states, which will feed into the product search query.
Create visual color and size selectors
Create visual color and size selectors
Create an Option Set called ProductColor with options like Red, Blue, Black, White. Add an attribute called hex_code (text) to store the color value (e.g., #FF0000 for Red). Inside the Colors section, add a Repeating Group of ProductColor options laid out as a grid (3-4 columns, fixed cell size of 40x40px). Style each cell as a circle with the background color set to Current cell's ProductColor's hex_code. Add a conditional: when Current cell's ProductColor's Display is in selected_colors, show a white checkmark icon overlay. Workflow on cell click: if the color is in selected_colors, remove it; if not, add it. For Sizes, create a ProductSize Option Set (XS, S, M, L, XL). Display as a row of rectangular buttons. Use the same toggle pattern: click adds or removes the size from selected_sizes, and a conditional changes the button style when selected.
Expected result: Colored circles and size buttons toggle on and off visually, updating their respective custom states with each click.
Combine all filters into one product search
Combine all filters into one product search
Select the product grid Repeating Group on the right side of the page. Set its Type of content to Product. Set the data source to: Do a search for Product with these constraints: category is in This page's selected_categories, price >= This page's min_price, price <= This page's max_price, color is in This page's selected_colors, size is in This page's selected_sizes. Critically, check the 'Ignore empty constraints' checkbox. This means when a filter state is empty (no categories selected, no min price entered, etc.), that constraint is ignored and all products pass through. The result is that only active filters restrict the results. Above the Repeating Group, add a Text element showing the result count: 'Showing X products' where X is the Repeating Group's List of Products :count.
Pro tip: The Ignore empty constraints option is the key to making multi-facet filtering work in Bubble. Without it, an empty list constraint would return zero results instead of all results.
Expected result: The product grid updates in real time as users check categories, adjust price, or select colors and sizes. Only active filters restrict the results.
Display active filter tags with remove and clear-all functionality
Display active filter tags with remove and clear-all functionality
Above the product grid and below the result count, add a horizontal Repeating Group to show active filter tags. Create a custom state called active_filter_tags (list of texts) on the page. Whenever a filter state changes, rebuild this list by combining all non-empty filter values into readable tags (e.g., 'Category: Electronics', 'Min price: $50', 'Color: Red', 'Size: M'). In each tag cell, display the tag text and an X icon button. The X button workflow: parse the tag type and remove the corresponding value from its custom state (e.g., remove 'Electronics' from selected_categories). Wire up the 'Clear All Filters' button at the top of the sidebar to reset all five custom states to empty and clear the active_filter_tags list. For a polished experience, add RapidDev-style smooth transitions: set the product Repeating Group to animate when its data source changes.
Expected result: Active filters appear as removable tag chips above the product grid. Clicking X on a tag removes that specific filter. Clear All resets everything.
Complete working example
1E-COMMERCE FILTER SYSTEM — FULL ARCHITECTURE2===============================================34OPTION SETS:5 ProductCategory: Electronics, Clothing, Home, Sports, ...6 ProductColor: Red (#FF0000), Blue (#0000FF), Black (#000),7 White (#FFF), Green (#00FF00), ...8 ProductSize: XS, S, M, L, XL910CUSTOM STATES (on page 'shop'):11 selected_categories — list of texts12 min_price — number13 max_price — number14 selected_colors — list of texts15 selected_sizes — list of texts16 active_filter_tags — list of texts1718FILTER SIDEBAR (left column, 250-300px):19 Clear All Filters button20 → Reset all 5 filter states to empty2122 Section: Categories23 RG of ProductCategory Option Set24 Each cell: Checkbox + category name + product count25 Checked → add to selected_categories26 Unchecked → remove from selected_categories2728 Section: Price Range29 Min Input + Max Input (integer format)30 Value changes → update min_price / max_price states31 Display: "$min — $max"3233 Section: Colors34 RG grid of ProductColor Option Set (40x40 circles)35 Background: hex_code attribute36 Click → toggle in/out of selected_colors37 Selected state: white checkmark overlay3839 Section: Sizes40 Row of ProductSize buttons41 Click → toggle in/out of selected_sizes42 Selected state: filled background4344PRODUCT GRID (right column):45 Data source: Do a search for Product46 category is in selected_categories47 price >= min_price48 price <= max_price49 color is in selected_colors50 size is in selected_sizes51 ✓ Ignore empty constraints = YES52 Sorted by: price ascending (or user choice)53 Items per page: 12-205455ACTIVE FILTER TAGS (above grid):56 Horizontal RG of active_filter_tags57 Each cell: tag text + X remove button58 X button → remove value from matching state5960RESULT COUNT:61 Text: "Showing X products"62 X = Product RG's list :countCommon mistakes when building an E-Commerce Filter System in Bubble
Why it's a problem: Using :filtered on the client instead of database search constraints
How to avoid: Always place filter logic inside the Do a search for Product constraints so filtering happens on the server before data is sent to the browser
Why it's a problem: Forgetting to enable Ignore empty constraints on the product search
How to avoid: Check the Ignore empty constraints checkbox on the search so unselected filter groups do not restrict the results
Why it's a problem: Recalculating product counts on every single filter change
How to avoid: Show static counts based on the full catalog, or only update counts for one filter group when a related filter changes
Why it's a problem: Not handling products that have multiple colors or sizes
How to avoid: Use list fields (colors: list of texts, sizes: list of texts) on Product and use the 'contains' constraint in the search to match any value in the list
Best practices
- Use Do a search for with server-side constraints and Ignore empty constraints instead of client-side :filtered for fast performance
- Show product counts next to each filter option so users can see how many results each choice produces before clicking
- Display active filters as removable tag chips above the results so users always know which filters are applied
- Provide a Clear All button at the top of the sidebar for a one-click reset of every filter
- Use Option Sets for categories, colors, and sizes so filter values load instantly without a database lookup
- Paginate the product grid to 12-20 items per page to keep the page responsive even with large catalogs
- Store filter state in URL parameters so users can bookmark or share a filtered view with others
- Add a sort dropdown (price low to high, newest, popularity) alongside the filters for a complete shopping experience
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I want to build a product filter system in my Bubble.io e-commerce app with checkbox category filters showing product counts, a price range with min and max inputs, visual color circle selectors, and size buttons. All filters should combine into one efficient search and display removable filter tags. Design the data model and outline every workflow.
Build a product filter system for my e-commerce page. Create an Option Set for categories, colors (with hex_code attribute), and sizes. Add a filter sidebar with checkboxes for categories, min/max price inputs, color circles, and size buttons. Combine all filters into a single Do a search for Product with Ignore empty constraints. Show active filter tags above the product grid with remove buttons.
Frequently asked questions
How many filter categories can I add before performance suffers?
There is no hard limit in Bubble. However, more than six or seven filter groups tend to overwhelm users. Focus on the attributes that matter most for your product type and consider collapsing less-used filters behind a Show More toggle.
Can I save filter selections so users can return to them later?
Yes. Store the custom state values in URL parameters using Bubble's Go to page action with query strings. Users can bookmark the URL or share it. For logged-in users, you can also create a SavedFilter Data Type.
Does changing a filter cause a full page reload?
No. Custom states update instantly on the client, and the Repeating Group's data source re-evaluates automatically. The product grid refreshes without a page reload, typically in 100-500ms.
Can I add a star rating filter?
Yes. Add a rating field (number) to your Product Data Type and a Minimum Rating dropdown or star selector to the sidebar. Include rating >= selected_rating as an additional search constraint on the product query.
How do I handle products that come in multiple colors?
Use a list of texts field for colors on the Product Data Type. In the search constraint, use the contains operator to check if the product's color list contains any of the selected colors.
Can RapidDev help build an advanced filtering system?
Yes. RapidDev builds production-grade filter systems in Bubble including faceted search with dynamic counts, URL-based filter persistence, sort controls, and responsive mobile filter drawers.
Should I show zero-result categories or hide them?
Best practice is to show them but gray them out with the count displayed as zero. This tells users the category exists but has no matching products given the other active filters, which is more informative than hiding the option entirely.
Can I add a text search alongside the facet filters?
Yes. Add a Search Input above the product grid and include a name contains Search Input's value constraint on the product search. It combines naturally with the other filter constraints.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation