Server-Side Search With searchCriteria Filters
Server-Side Search With searchCriteria Filters
~13 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
To close out the functional chapters, let's build REAL search – not client-side filtering of an already-loaded array, but a request that lets Magento ITSELF search for matching products.
Why server-side instead of client-side filtering?
A client-side filter (products.filter(p => p.name.includes(query))) would ONLY search WITHIN the already-loaded 12 products of the current page – a product on page 5 simply wouldn't be found by a search on page 1. A server-side search, on the other hand, searches the ENTIRE product catalog, independent of pagination.
filterGroups: Magento's search syntax
searchCriteria[filterGroups][0][filters][0][field]=name
searchCriteria[filterGroups][0][filters][0][value]=%25Shirt%25
searchCriteria[filterGroups][0][filters][0][condition_type]=likecondition_type=like combined with % wildcards in value (URL-encoded as %25) corresponds to a SQL "LIKE %Shirt%" search – finding "Shirt" ANYWHERE in the product name, not just at the start.
Extending fetchProducts() with a search option
// ... previous code ...
export async function fetchProducts({
page = 1,
sortField = 'name',
direction = 'ASC',
query = '',
} = {}) {
const params = new URLSearchParams({
'searchCriteria[pageSize]': PAGE_SIZE,
'searchCriteria[currentPage]': page,
'searchCriteria[sortOrders][0][field]': sortField,
'searchCriteria[sortOrders][0][direction]': direction,
});
if (query.trim() !== '') {
params.set('searchCriteria[filterGroups][0][filters][0][field]', 'name');
params.set('searchCriteria[filterGroups][0][filters][0][value]', `%${query}%`);
params.set('searchCriteria[filterGroups][0][filters][0][condition_type]', 'like');
}
const data = await magentoFetch(`/products?${params.toString()}`);
return {
products: data.items,
totalCount: data.total_count,
pageCount: Math.ceil(data.total_count / PAGE_SIZE),
};
}components/SearchBar.jsx
import { useState } from 'react';
function SearchBar({ onSearch }) {
const [input, setInput] = useState('');
function submit(event) {
event.preventDefault();
onSearch(input);
}
return (
<form onSubmit={submit}>
<input
type="text"
placeholder="Search products..."
value={input}
onChange={(event) => setInput(event.target.value)}
/>
<button type="submit">Search</button>
</form>
);
}
export default SearchBar;ProductListPage.jsx: integrating search
import { useEffect, useState } from 'react';
import { fetchProducts } from '../api/magentoApi';
import Pagination from '../components/Pagination';
import ProductCard from '../components/ProductCard';
import SearchBar from '../components/SearchBar';
function ProductListPage() {
const [products, setProducts] = useState([]);
const [pageCount, setPageCount] = useState(1);
const [currentPage, setCurrentPage] = useState(1);
const [query, setQuery] = useState('');
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
async function load() {
const result = await fetchProducts({ page: currentPage, query });
setProducts(result.products);
setPageCount(result.pageCount);
setLoading(false);
}
load();
}, [currentPage, query]);
function newSearch(term) {
setQuery(term);
setCurrentPage(1); // IMPORTANT: reset to page 1 on a new search
}
return (
<div>
<h1>Products</h1>
<SearchBar onSearch={newSearch} />
{loading ? (
<p>Loading products...</p>
) : (
<>
<ul>
{products.map((product) => (
<ProductCard key={product.sku} product={product} />
))}
</ul>
<Pagination
currentPage={currentPage}
pageCount={pageCount}
onPageChange={setCurrentPage}
/>
</>
)}
</div>
);
}
export default ProductListPage;[currentPage, query] as dependencies ensure a new request fires both on page change AND on a new search – and setCurrentPage(1) in newSearch() prevents the subtle bug of landing on page 3 of a new, shorter result list that might only have 1 page.
Tipp: Search for a word fragment you'd expect in SEVERAL of your real product names – if only matching results show up, AND pagination works correctly within the filtered results, server-side search is working as intended.