Разработка фильтра товаров на React для 1С-Битрикс
Штатный компонент bitrix:catalog.smart.filter работает через server-side рендеринг с полной перезагрузкой страницы или AJAX-подменой HTML. При каждом изменении фильтра — HTTP-запрос, серверный рендеринг, замена блока в DOM. На каталоге в 50 000 SKU с 30+ характеристиками время ответа составляет 800–2000 мс, и это ещё без учёта времени рендеринга на фронте.
React-фильтр меняет подход: UI обновляется мгновенно, запрос к серверу отправляется с debounce 300–500 мс, результаты обновляются в фоне. Пользователь видит отклик сразу, а не ждёт round-trip до сервера.
Архитектура React-фильтра
Фильтр состоит из трёх независимых частей: компонент фильтра (чекбоксы, ренджи, сортировка), компонент каталога (список товаров, пагинация), URL-синхронизация (состояние фильтра отражается в URL для шаринга и SEO).
Состояние фильтра синхронизируется с URL через useSearchParams из React Router или нативный URLSearchParams. Это критично: пользователь должен иметь возможность скопировать URL с применёнными фильтрами.
// Хук синхронизации фильтра с URL
function useFilterState(initialFilters: FilterState) {
const [searchParams, setSearchParams] = useSearchParams();
const filters = useMemo(() => {
return parseFiltersFromParams(searchParams, initialFilters);
}, [searchParams]);
const setFilters = useCallback((newFilters: Partial<FilterState>) => {
const params = buildParamsFromFilters({ ...filters, ...newFilters });
setSearchParams(params, { replace: true }); // replace, не push — не засоряем историю
}, [filters, setSearchParams]);
return [filters, setFilters] as const;
}
API на стороне Битрикс
Серверный endpoint принимает параметры фильтра и возвращает товары + фасетные счётчики (сколько товаров по каждому значению характеристики).
public function getProductsAction(array $filter = [], int $page = 1): array
{
// Строим Битрикс-фильтр из параметров запроса
$bxFilter = $this->buildBitrixFilter($filter);
// Товары
$result = \CIBlockElement::GetList(
['SORT' => 'ASC'],
$bxFilter,
false,
['nPageSize' => 24, 'iNumPage' => $page],
['ID', 'NAME', 'PREVIEW_PICTURE', 'DETAIL_PAGE_URL',
'CATALOG_PRICE_1', 'PROPERTY_BRAND', 'PROPERTY_COLOR']
);
$products = [];
while ($product = $result->GetNextElement()) {
$fields = $product->GetFields();
$props = $product->GetProperties();
$products[] = $this->formatProduct($fields, $props);
}
// Фасетные счётчики для обновления вариантов фильтра
$facets = $this->getFacets($bxFilter, $filter);
return [
'products' => $products,
'total' => $result->SelectedRowsCount(),
'facets' => $facets,
];
}
Для фасетных счётчиков используется \Bitrix\Iblock\Component\Tools или прямые запросы к таблицам b_iblock_element_property с GROUP BY — зависит от нагрузки и объёма каталога.
Типы фильтров и их реализация
Чекбокс-группы (бренд, цвет, материал) — самый распространённый тип. Значения приходят из свойств инфоблока. На фронте — список с поиском внутри группы при большом количестве значений (20+).
Диапазон цены — ценовой слайдер с двумя ручками. Библиотека rc-slider или нативная реализация на CSS custom properties. Запрос отправляется с debounce 500 мс — иначе каждый пиксель движения слайдера триггерит запрос.
Рейтинг — звёздный рейтинг как фильтр (от N звёзд).
Размерная сетка — кнопки-переключатели. Особенность: доступность конкретного размера зависит от остатков, поэтому неактивные значения должны подсвечиваться серым, а не просто отсутствовать.
Кейс: фильтр для маркетплейса одежды
Мультивендорный каталог, ~180 000 SKU, 45 характеристик. Задача: фильтр должен работать мгновенно, поддерживать многоуровневые зависимости (выбор категории меняет доступные фильтры) и отражать состояние в URL для SEO.
Штатный SmartFilter Битрикс давал время ответа 1.4–2.1 сек, при этом фасетные счётчики пересчитывались при каждом запросе и становились узким местом.
Решение:
-
Фасеты вынесены в отдельный кешируемый endpoint. Счётчики пересчитываются через очередь (
\Bitrix\Main\Application::getInstance()->addBackgroundJob()) после изменения остатков, а не в реальном времени на каждый запрос. -
На фронте — optimistic UI: чекбокс отмечается мгновенно, счётчик товаров обновляется с небольшой задержкой. Пользователь не ждёт подтверждения от сервера для визуального отклика.
-
Мобильная версия фильтра — отдельный компонент с bottom sheet (выезжает снизу). Десктоп — сайдбар. Переключение через CSS media query + React context.
-
Пустые результаты — не белый экран, а блок с предложением расширить фильтр (убрать одну из характеристик). React анализирует, какой последний выбранный параметр даёт 0 результатов, и предлагает его убрать.
| Характеристика | SmartFilter | React-фильтр |
|---|---|---|
| Время отклика на чекбокс | 1400–2100 мс | 0 мс (UI), 280–400 мс (данные) |
| Обновление счётчиков | При каждом запросе | Из кеша, обновление фоном |
| Глубина линк-шаринга | Частичная | Полная (все параметры в URL) |
| Мобильный UX | Отдельная страница | Bottom sheet без перехода |
SEO и SSR
Фильтр на React создаёт SEO-проблему: поисковик не всегда выполняет JavaScript. Решений несколько.
SSR через отдельный рендерер — тяжело, требует Node.js-инфраструктуры рядом с Битрикс.
Prerendering — SPA рендерится через headless Chrome при краулинге. Реализуется через Prerender.io или nginx-конфиг, который определяет User-Agent бота и проксирует запрос на рендерер.
Гибридный подход (рекомендуемый): страницы с SEO-ценными фильтрами (категория + бренд) имеют статические PHP-URL, которые рендерит Битрикс. React-фильтр работает поверх этих страниц, но начальное состояние берёт из PHP-шаблона (переданный в window.__INITIAL_STATE__). Боты видят HTML, пользователи — интерактивный React.
Оптимизация производительности
Бесконечная прокрутка vs. пагинация: для мобильных — infinite scroll через IntersectionObserver, для десктопа — пагинация (SEO + возможность вернуться к конкретной странице).
Виртуализация списка при большом количестве результатов (react-virtual или @tanstack/react-virtual) — при отображении 200+ карточек без виртуализации DOM становится тяжёлым.
React Query для кеширования: переход между страницами каталога не делает повторный запрос, если данные в кеше свежие (staleTime: 30_000).
Состав работ
- Анализ структуры каталога: характеристики, типы фильтров, объём данных
- API-контроллер: фильтрация, пагинация, фасеты, кеширование
- Разработка React: компоненты фильтра, каталога, синхронизация с URL
- SEO-стратегия: prerendering или гибридный подход
- Тестирование производительности под нагрузкой
Сроки: базовый фильтр (чекбоксы + диапазон цены) — 2–3 недели. Полнофункциональный с фасетами, мобильным UX и SEO-стратегией — 5–8 недель.







