Guides / Building Search UI / Going further

Conditional display in React InstantSearch

This is the React InstantSearch v7 documentation. React InstantSearch v7 is the latest version of React InstantSearch and the stable version of React InstantSearch Hooks.

If you were using React InstantSearch v6, you can upgrade to v7.

If you were using React InstantSearch Hooks, you can still use the React InstantSearch v7 documentation, but you should check the upgrade guide for necessary changes.

If you want to keep using React InstantSearch v6, you can find the archived documentation.

This guide describes what to do when there are no results, when there’s no query, or when there are errors. Sometimes, though, users may not get any hits if their device can’t access the network or the network connection is slow.

If you want to feature content in your search results based on a set of conditions, you can use Algolia Rules to:

To learn how to suppress InstantSearch’s initial search query, check out the conditional requests guide.

Handling no results

Since not all queries lead to results, it’s essential to let users know when this happens by providing hints on how to adjust the query.

Display a message

The easiest way to display a fallback message when a query doesn’t return results is to use the useInstantSearch() Hook to create a wrapper component:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function App(props) {
  return (
    <InstantSearch {...props}>
      <SearchBox />
      <NoResultsBoundary fallback={<NoResults />}>
        <Hits />
      </NoResultsBoundary>
    </InstantSearch>
  );
}

function NoResultsBoundary({ children, fallback }) {
  const { results } = useInstantSearch();

  // The `__isArtificial` flag makes sure not to display the No Results message
  // when no hits have been returned.
  if (!results.__isArtificial && results.nbHits === 0) {
    return (
      <>
        {fallback}
        <div hidden>{children}</div>
      </>
    );
  }

  return children;
}

function NoResults() {
  const { indexUiState } = useInstantSearch();

  return (
    <div>
      <p>
        No results for <q>{indexUiState.query}</q>.
      </p>
    </div>
  );
}

You can pass anything to display results, including <InfiniteHits> or a custom component that uses the useHits() Hook.

Follow best practices for showing and hiding React InstantSearch widgets to prevent undesirable additional search requests.

Let users reset filters and facets

If users apply too many filters, they may not find any results. You should account for this by letting them reset filters from the “no results” display so they can start another search.

Do this with the <ClearRefinements> widget.

1
2
3
4
5
6
7
8
9
10
11
12
function NoResults() {
  const { indexUiState } = useInstantSearch();

  return (
    <div>
      <p>
        No results for <q>{indexUiState.query}</q>.
      </p>
      <ClearRefinements excludedAttributes={[]} />
    </div>
  );
}

Handling empty queries

By default, InstantSearch always shows you results, even when the query is empty. Depending on your use case and how you build your UI, you may only want to show results when there’s a query.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function App(props) {
  return (
    <InstantSearch {...props}>
      <SearchBox />
      <EmptyQueryBoundary fallback={null}>
        <Hits />
      </EmptyQueryBoundary>
    </InstantSearch>
  );
}

function EmptyQueryBoundary({ children, fallback }) {
  const { indexUiState } = useInstantSearch();

  if (!indexUiState.query) {
    return (
      <>
        {fallback}
        <div hidden>{children}</div>
      </>
    );
  }

  return children;
}

Follow best practices for showing and hiding React InstantSearch widgets to prevent undesirable additional search requests.

Handling errors

When an error occurs, you can display a specific piece of content to help users return to the standard state.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import React, { useEffect, useState } from 'react';
import * as Toast from '@radix-ui/react-toast';
import { useInstantSearch } from 'react-instantsearch';

function SearchErrorToast() {
  const { addMiddlewares } = useInstantSearch();
  const [error, setError] = useState(null);

  useEffect(() => {
    const middleware = ({ instantSearchInstance }) => {
      function handleError(searchError) {
        setError(searchError);
      }

      return {
        subscribe() {
          instantSearchInstance.addListener('error', handleError);
        },
        unsubscribe() {
          instantSearchInstance.removeListener('error', handleError);
        },
      };
    };

    return addMiddlewares(middleware);
  }, [addMiddlewares]);

  if (!error) {
    return null;
  }

  return (
    <Toast.Provider>
      <Toast.Root
        onOpenChange={(isOpen) => {
          if (!isOpen) {
            setError(null);
          }
        }}
      >
        <Toast.Title>{error.name}</Toast.Title>
        <Toast.Description>{error.message}</Toast.Description>
      </Toast.Root>

      <Toast.Viewport />
    </Toast.Provider>
  );
}

function App(props) {
  return (
    <InstantSearch {...props}>
      <SearchErrorToast />
      <SearchBox />
      <Hits />
    </InstantSearch>
  );
}
Did you find this page helpful?