> Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Add Data Search and Filtering Functionality ## Article: Tutorial | Add Data Search and Filtering Functionality ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/data/tutorial-add-data-search-and-filtering-functionality.md ## Article Content: # Tutorial | Add Data Search and Filtering Functionality When you have a database collection with lots of content, site visitors need an easy way to find what they're looking for. You can add powerful search and filtering capabilities to your site, transforming static content displays into interactive, user-friendly experiences. This tutorial demonstrates how to build: - **Real-time text search** - Site visitors type in a search field and see results update instantly as they type. - **Dropdown filtering** - Site visitors select from predefined categories using a dropdown menu populated with unique values from your collection. You'll learn to query collections, handle user input, and create interfaces that enhance the user experience. We'll use the following steps to query a database collection and dynamically display matching results: 1. [Set up site elements](#step-1--add-elements-to-your-site). 1. [Query and display all content on page load](#step-2--query-and-display-all-content-on-page-load). 1. [Add search and filter functionality](#step-3--filter-the-repeater). > The code in this article was written using the following module versions: > > - @wix/data (v1.0.244) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). ## Before you begin You need to note the following points before starting to code: - Create a [collection](https://support.wix.com/en/article/cms-formerly-content-manager-creating-a-collection) on your site. ## Step 1 | Add elements to your page This step sets up the user interface elements needed for search and filtering functionality. 1. Add a [repeater](https://support.wix.com/en/article/studio-editor-adding-and-customizing-repeaters) to your page. Set the repeater ID to `articlesList`. Inside the repeater, add: - An image element. Set the image ID to `itemImage`. - Two text elements. Set the text IDs to `itemCountry` and `itemContent` 2. Add an input field for searching. Set the input ID to `inputTitle`. 3. Add a dropdown element. Set the dropdown ID to `inputContinent`. ## Step 2 | Query and display all content on page load This step retrieves all items from your collection and prepares them for display. When the page loads, you'll display all the items before any filtering is applied. To query a collection, you'll need to get the collection ID. The way you get the ID depends on the editor you're using: ::::tabs :::Wix-Studio 1. Click on the **CMS** tab in the Code panel. 1. Hover over your collection, click the **Show More** icon, and select **Edit settings**. 1. Copy the collection ID to use in your code. ::: :::Wix-Editor 1. Click on the **Databases** tab in the Code sidebar. 1. Hover over your collection, click the **Show More** icon, and select **Edit settings**. 1. Copy the collection ID to use in your code. ::: :::: Now that you have the collection ID, you can query your data: 1. Import the [`@wix/data`](https://dev.wix.com/docs/sdk/backend-modules/data/items/introduction.md) module, which provides the [`query()`](https://dev.wix.com/docs/sdk/backend-modules/data/items/query.md) method for retrieving collection data. Also, save the collection ID to the `collectionName` variable. ```javascript import { items } from "@wix/data"; const collectionName = "Articles"; ``` 2. Define a function that runs the query using the ID you retrieved above. Set the repeater's [data](https://www.wix.com/velo/reference/$w/repeater/data) property to your collection results: ```javascript async function loadAllResults() { const results = await items.query(collectionName).find(); $w("#articlesList").data = results.items; } ``` The query returns an array of [`items`](https://dev.wix.com/docs/sdk/backend-modules/data/items/query.md), where each item represents an item from your collection. Collection [field IDs](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collection-fields.md#field-ids) become object properties, so fields such as `image`, `country`, `content` appear as properties with the same names in each result object. 3. Use the [`onItemReady()`](https://www.wix.com/velo/reference/$w/repeater/onitemready) event handler to map your collection field values to specific repeater elements. This event runs automatically for each new element in the repeater's `.data` array. The handler receives an [`$item`](https://www.wix.com/velo/reference/$w/repeater/introduction#$w_repeater_introduction_repeated-item-scope) selector, which selects specific instances of repeated elements. In this example, `image`, `country`, and `content` are [field IDs](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/work-with-data/introduction/about-collection-fields.md#field-ids) from your database collection: ```javascript function initRepeater() { $w("#articlesList").onItemReady(($item, itemData) => { $item("#itemImage").src = itemData.image; $item("#itemCountry").text = itemData.country; $item("#itemContent").text = itemData.content; }); } ``` 4. Define a function to query your collection and populate the dropdown with all unique, non-empty `continent` values. Add a default option labeled "All" with the value `All`. This option allows site visitors to view all items, unfiltered. ```javascript async function initDropdown() { const queryResults = await items.query(collectionName).find(); const uniqueContinents = [ ...new Set(queryResults.items.map((item) => item.continent)), ] .filter(Boolean) .sort((a, b) => a.localeCompare(b)); $w("#inputContinent").options = [ { value: "All", label: "All" }, ...uniqueContinents.map((c) => ({ value: c, label: c })), ]; } ``` 5. Call the `initRepeater()`, `loadAllResults()`, and `initDropdown()` functions in the `$w.onReady()` handler: ```javascript $w.onReady(async function () { await initDropdown(); initRepeater(); await loadAllResults(); }); ``` ## Step 3 | Filter the repeater This step adds the interactive functionality that allows site visitors to search and filter the displayed content in real time. 1. Define a function called `buildFilterAndPopulateRepeater()`. It filters your repeater results by checking if the "country" field contains the input text. When a dropdown value is selected, this function filters the collection and updates the repeater. Also, set the repeater's [data](https://www.wix.com/velo/reference/$w/repeater/data) property to your collection results: ```javascript async function buildFilterAndPopulateRepeater(inputSearch, continent) { let dataQuery = items.query(collectionName); if (inputSearch) { dataQuery = dataQuery.contains("country", inputSearch); } if (continent && continent !== "All") { dataQuery = dataQuery.contains("continent", continent); } const results = await dataQuery.find(); $w("#articlesList").data = results.items; } ``` 2. For filtering by input, add an event handler that listens for any input changes in the search field. To prevent triggering too many queries, use a short delay after each keystroke. This technique, called [debouncing](https://developer.mozilla.org/en-US/docs/Glossary/Debounce), improves both performance and user experience by ensuring the search only runs after the site visitor has stopped typing for a specified period: ```javascript let debounceTimer; $w("#inputTitle").onInput(() => { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { const searchValueContinent = $w("#inputContinent").value; const searchValueInput = $w("#inputTitle").value; buildFilterAndPopulateRepeater(searchValueInput, searchValueContinent); }, 400); }); ``` 3. For filtering by dropdown, add an event handler that listens for any changes in the dropdown. ```javascript $w("#inputContinent").onChange(() => { const searchValueContinent = $w("#inputContinent").value; const searchValueInput = $w("#inputTitle").value; buildFilterAndPopulateRepeater(searchValueInput, searchValueContinent); }); ``` ## Complete code Here's the complete working code: ```javascript import { items } from "@wix/data"; const collectionName = "Articles"; let debounceTimer; $w.onReady(async function () { await initDropdown(); initRepeater(); await loadAllResults(); }); $w("#inputTitle").onInput(() => { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { const searchValueContinent = $w("#inputContinent").value; const searchValueInput = $w("#inputTitle").value; buildFilterAndPopulateRepeater(searchValueInput, searchValueContinent); }, 400); }); $w("#inputContinent").onChange(() => { const searchValueContinent = $w("#inputContinent").value; const searchValueInput = $w("#inputTitle").value; buildFilterAndPopulateRepeater(searchValueInput, searchValueContinent); }); async function initDropdown() { const queryResults = await items.query(collectionName).find(); const uniqueContinents = [ ...new Set(queryResults.items.map((item) => item.continent)), ] .filter(Boolean) .sort((a, b) => a.localeCompare(b)); $w("#inputContinent").options = [ { value: "All", label: "All" }, ...uniqueContinents.map((c) => ({ value: c, label: c })), ]; } async function loadAllResults() { const results = await items.query(collectionName).find(); $w("#articlesList").data = results.items.length; } async function buildFilterAndPopulateRepeater(inputSearch, continent) { let dataQuery = items.query(collectionName); if (inputSearch) { dataQuery = dataQuery.contains("country", inputSearch); } if (continent && continent !== "All") { dataQuery = dataQuery.contains("continent", continent); } const results = await dataQuery.find(); $w("#articlesList").data = results.items.length; } function initRepeater() { $w("#articlesList").onItemReady(($item, itemData) => { $item("#itemImage").src = itemData.image; $item("#itemCountry").text = itemData.country; $item("#itemContent").text = itemData.content; }); } ``` ## See also - [$item](https://www.wix.com/velo/reference/$w/repeater/introduction#$w_repeater_introduction_repeated-item-scope) - [Data Items API](https://dev.wix.com/docs/sdk/backend-modules/data/items/introduction.md)