Adapting desktop filter functionality for mobile interfaces helps you maintain consistent filtering capabilities across all devices. When mobile users access the same powerful refinement tools as desktop users, you help improve mobile product discovery and tend to enhance mobile conversion rates by ensuring that customers can narrow results effectively regardless of device.
Consistent filter experiences across devices enable you to provide equal product discovery capabilities to all customers. This helps improve mobile engagement and conversion by ensuring that mobile shoppers aren't limited in their ability to find specific products, supporting better overall shopping experiences across your entire customer base.
There are 2 options to utilize desktop filters within the mobile filter menu:
Option 1 - Default Logic (when only one filter can be expanded)
Option 2 - allows to expand multiple filters at the same time
Option 1
components/search/MobileFacets/index.tsxstyle.css
/* VARIANT 1 */import DesktopFacets from 'components/search/DesktopFacets';{/*... some code ...*/} return ( {/* Place DesktopFacets component and remove Branch */} <DesktopFacets /> {/* <Branch config={config} theme={theme} selectFacet={selectFacet} active={facets.find((f) => f.get('name') === activeFacetName)} facets={facets} condition={activeFacet} right={FacetTitles} left={FacetContent} /> */} {/* ...rest */}
Option 2
components/search/MobileFacets/index.tsxcomponents/search/DesktopFacets/index.tsxstyle.css
import DesktopFacets from 'components/search/DesktopFacets';{/* ...some code... */} /* Check For Mobile Version */ const isMobile = window.innerWidth < config.toJS().mobileBreakpoint; return ({/* some code */} {/* Place DesktopFacets component and remove Branch, pass down props */} <DesktopFacets isMobile={isMobile} /> {/* <Branch config={config} theme={theme} selectFacet={selectFacet} active={facets.find((f) => f.get('name') === activeFacetName)} facets={facets} condition={activeFacet} right={FacetTitles} left={FacetContent} /> */} {/* ...rest */}
Adding Breadcrumbs
components/search/MobileFacets/index.tsx
//import Breadcrubms componentimport Breadcrumbs from 'components/Breadcrumbs';/place it wherever you need<Breadcrumbs />
Complete Example
Option 1 - Default Logic (when only one filter can be expanded)
Option 2 - allows to expand multiple filters at the same time
Option 1
components/search/MobileFacets/index.tsxstyle.css
/** * @module components /search/MobileFacets *//*import Branch from 'components/common/Branch';import FacetTitles from 'components/search/MobileFacets/Titles';import Component from 'components/Facet/Component';*/import Button from 'components/Button';import cx from 'classnames';import Icon from 'components/Icon';import Text from 'components/Text';import { ThemedSFCProps, IFacet, MJSConfiguration, MJSValue } from 'types';import { List } from 'immutable';import styles from 'components/search/MobileFacets/styles.css';import { useFacets, useQuery } from '@findify /react-connect';import { memo, useCallback, useMemo, useState } from 'react';import { Immutable } from '@findify /store-configuration';import useTranslations from 'helpers/useTranslations';import { Facet } from '@findify /react-connect/types/immutable/facets';//import DesktopFacets and Breadcrumbsimport DesktopFacets from 'components/search/DesktopFacets';import Breadcrumbs from 'components/Breadcrumbs';/** Props that FacetContent accepts */export interface IFacetContentProps extends ThemedSFCProps { /** Currently active facet */ active: Facet; /** MJS Configuration */ config: Immutable.SearchConfig;}{/* const FacetContent = ({ active, config, theme = styles,}: IFacetContentProps) => { const _config = config.merge( config.getIn(['facets', 'filters', active.get('name')]) ); return ( <div className={cx(theme.container, theme[_config.get('type')])}> <Component isExpanded isMobile type={_config.get('type')} facet={active} config={_config} /> </div> );};*/}/** Props that MobileFacets view accepts */export interface IMobileFacetsProps extends ThemedSFCProps { /** immutable.List() of Facets */ facets: List<IFacet>; /** Currently active facet */ activeFacet?: IFacet; /** Method used to select a facet */ selectFacet: (name?: string) => any; /** Method used to reset facet */ onReset: () => any; /** MJS API Request Metadata */ meta: Map<string, MJSValue>; /** Method used for hiding modal / drawer */ hideModal: (name: string) => any; /** Total filters selected */ total: number; /** Filters selected for active facet */ filtersSelected: number;}export default memo(({ theme = styles, hideModal }: IMobileFacetsProps) => { const { facets, config, update } = useFacets<Immutable.SearchConfig>(); const { query } = useQuery(); const translate = useTranslations(); const [activeFacetName, setActiveFacet] = useState<string | null>(null); const total = useMemo( () => query .get('filters') ?.reduce( (acc, filter) => acc + (/category[2-9]/.test(filter.get('name')) ? 0 : filter.get('values').size), 0 ) || 0, [query] ); const activeFacet = useMemo( () => facets.find((f) => f.get('name') === activeFacetName), [activeFacetName] ); const filtersSelected = useMemo(() => { if (!activeFacet) return 0; return activeFacet.get('values').filter((item) => item.get('selected')) .size; }, [activeFacet]); const selectFacet = useCallback((name) => { setActiveFacet(name || null); }, []); const onReset = useCallback(() => { update('filters', (f) => f?.clear()); }, []); return ( <div className={cx(theme.modal, 'mobile')}> <div className={theme.header}> <div className={theme.title}> <Text primary uppercase display-if={!activeFacet}> {translate('facets.filters')} </Text> <Text secondary uppercase display-if={!activeFacet && total} className={theme.filterCount} > ({total}) </Text> <Text primary uppercase display-if={activeFacet}> {config .getIn(['facets', 'filters', activeFacet?.get('name')]) ?.get('label')} </Text> <Text secondary uppercase display-if={activeFacet && filtersSelected} className={theme.filterCount} > ({filtersSelected}) </Text> </div> <Button onClick={activeFacet ? selectFacet : hideModal} className={theme.backButton} > <Icon name="ArrowBack" title={translate('facets.back')} /> </Button> <Button display-if={query?.get('filters')?.size} onClick={onReset}> <Text secondary uppercase> {translate('facets.clearAll')} </Text> </Button> </div> <div className={theme.body}> {/* Place DesktopFacets component and remove Branch */} <Breadcrumbs /> <DesktopFacets /> {/* <Branch config={config} theme={theme} selectFacet={selectFacet} active={facets.find((f) => f.get('name') === activeFacetName)} facets={facets} condition={activeFacet} right={FacetTitles} left={FacetContent} /> */} </div> <Button className={theme.footer} onClick={activeFacet ? selectFacet : hideModal} > {activeFacet ? translate('facets.done') : translate('facets.seeResults')} </Button> </div> );});
Option 2
components/search/MobileFacets/index.tsxcomponents/search/DesktopFacets/index.tsxstyle.css
/** * @module components /search/MobileFacets *//*import Branch from 'components/common/Branch';import FacetTitles from 'components/search/MobileFacets/Titles';import Component from 'components/Facet/Component';*/import Button from 'components/Button';import cx from 'classnames';import Icon from 'components/Icon';import Text from 'components/Text';import { ThemedSFCProps, IFacet, MJSConfiguration, MJSValue } from 'types';import { List } from 'immutable';import styles from 'components/search/MobileFacets/styles.css';import { useFacets, useQuery } from '@findify /react-connect';import { memo, useCallback, useMemo, useState } from 'react';import { Immutable } from '@findify /store-configuration';import useTranslations from 'helpers/useTranslations';import { Facet } from '@findify /react-connect/types/immutable/facets';//import DesktopFacets and Breadcrumbsimport DesktopFacets from 'components/search/DesktopFacets';import Breadcrumbs from 'components/Breadcrumbs';/** Props that FacetContent accepts */export interface IFacetContentProps extends ThemedSFCProps { /** Currently active facet */ active: Facet; /** MJS Configuration */ config: Immutable.SearchConfig;}{/* const FacetContent = ({ active, config, theme = styles,}: IFacetContentProps) => { const _config = config.merge( config.getIn(['facets', 'filters', active.get('name')]) ); return ( <div className={cx(theme.container, theme[_config.get('type')])}> <Component isExpanded isMobile type={_config.get('type')} facet={active} config={_config} /> </div> );};*/}/** Props that MobileFacets view accepts */export interface IMobileFacetsProps extends ThemedSFCProps { /** immutable.List() of Facets */ facets: List<IFacet>; /** Currently active facet */ activeFacet?: IFacet; /** Method used to select a facet */ selectFacet: (name?: string) => any; /** Method used to reset facet */ onReset: () => any; /** MJS API Request Metadata */ meta: Map<string, MJSValue>; /** Method used for hiding modal / drawer */ hideModal: (name: string) => any; /** Total filters selected */ total: number; /** Filters selected for active facet */ filtersSelected: number;}export default memo(({ theme = styles, hideModal }: IMobileFacetsProps) => { const { facets, config, update } = useFacets<Immutable.SearchConfig>(); const { query } = useQuery(); const translate = useTranslations(); const [activeFacetName, setActiveFacet] = useState<string | null>(null); const total = useMemo( () => query .get('filters') ?.reduce( (acc, filter) => acc + (/category[2-9]/.test(filter.get('name')) ? 0 : filter.get('values').size), 0 ) || 0, [query] ); const activeFacet = useMemo( () => facets.find((f) => f.get('name') === activeFacetName), [activeFacetName] ); const filtersSelected = useMemo(() => { if (!activeFacet) return 0; return activeFacet.get('values').filter((item) => item.get('selected')) .size; }, [activeFacet]); const selectFacet = useCallback((name) => { setActiveFacet(name || null); }, []); const onReset = useCallback(() => { update('filters', (f) => f?.clear()); }, []); /* Check For Mobile Version */ const isMobile = window.innerWidth < config.toJS().mobileBreakpoint; return ( <div className={cx(theme.modal, 'mobile')}> <div className={theme.header}> <div className={theme.title}> <Text primary uppercase display-if={!activeFacet}> {translate('facets.filters')} </Text> <Text secondary uppercase display-if={!activeFacet && total} className={theme.filterCount} > ({total}) </Text> <Text primary uppercase display-if={activeFacet}> {config .getIn(['facets', 'filters', activeFacet?.get('name')]) ?.get('label')} </Text> <Text secondary uppercase display-if={activeFacet && filtersSelected} className={theme.filterCount} > ({filtersSelected}) </Text> </div> <Button onClick={activeFacet ? selectFacet : hideModal} className={theme.backButton} > <Icon name="ArrowBack" title={translate('facets.back')} /> </Button> <Button display-if={query?.get('filters')?.size} onClick={onReset}> <Text secondary uppercase> {translate('facets.clearAll')} </Text> </Button> </div> <div className={theme.body}> {/* Place DesktopFacets component and remove Branch, pass down props */} <Breadcrumbs /> <DesktopFacets isMobile={isMobile} /> {/* <Branch config={config} theme={theme} selectFacet={selectFacet} active={facets.find((f) => f.get('name') === activeFacetName)} facets={facets} condition={activeFacet} right={FacetTitles} left={FacetContent} /> */} </div> <Button className={theme.footer} onClick={activeFacet ? selectFacet : hideModal} > {activeFacet ? translate('facets.done') : translate('facets.seeResults')} </Button> </div> );});
Related Articles
Search & Discovery:
Product Recommendations:
Conversion Optimization: