> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# React Native

> How to create a React InstantSearch app using React Native.

export const SearchQuery = () => <Tooltip tip="The text users enter into a search box. In the Search API, this corresponds to the query parameter. A search query is often used with filters, facets, and other parameters, but these aren't part of the query text itself.">
    search query
  </Tooltip>;

export const Index = () => <Tooltip tip="An Algolia index is a searchable dataset that consists of records and configuration settings. These settings define how the records are searched and ranked.">
    index
  </Tooltip>;

export const Filter = () => <Tooltip tip="A filter is a condition that limits which records Algolia returns. Filters often use one or more facet-value pairs, such as brand:Apple AND color:red. You can also filter by numeric values, dates, tags, booleans, or geographic constraints." cta="Filtering" href="/doc/guides/managing-results/refine-results/faceting">
    filter
  </Tooltip>;

export const Application = () => <Tooltip tip="An Algolia application is a self-contained environment with its own indices, configuration, and API keys. Applications don't share data or settings with each other.">
    application
  </Tooltip>;

export const FlavorSwitcher = ({current, baseHref = "", options = [], label = "InstantSearch framework"}) => {
  if (options.length === 0) {
    return <div className="not-prose" role="alert" style={{
      margin: "0.25rem 0 1.5rem",
      padding: "0.75rem",
      border: "1px solid #f59e0b",
      borderRadius: "0.625rem",
      color: "inherit",
      fontSize: "0.875rem"
    }}>
        FlavorSwitcher requires at least one option.
      </div>;
  }
  const selected = options.find(option => option.value === current) ?? options[0];
  return <div className="not-prose mint-flavor-switcher">
      <style>{`
        .mint-flavor-switcher {
          --mfs-bg: #ffffff;
          --mfs-bg-hover: #f4f4f5;
          --mfs-bg-current: #eef2ff;
          --mfs-border: #d4d4d8;
          --mfs-fg: #18181b;
          --mfs-muted: #71717a;
          --mfs-accent: #4f46e5;
          position: relative;
          width: min(100%, 19rem);
          margin: 0.25rem 0 1.5rem;
          color: var(--mfs-fg);
          font-size: 0.875rem;
          line-height: 1.25rem;
        }

        .dark .mint-flavor-switcher {
          --mfs-bg: #18181b;
          --mfs-bg-hover: #27272a;
          --mfs-bg-current: #272747;
          --mfs-border: #3f3f46;
          --mfs-fg: #fafafa;
          --mfs-muted: #a1a1aa;
          --mfs-accent: #a5b4fc;
        }

        .mint-flavor-switcher details {
          position: relative;
        }

        .mint-flavor-switcher summary {
          display: flex;
          min-height: 2.75rem;
          box-sizing: border-box;
          align-items: center;
          justify-content: space-between;
          gap: 0.75rem;
          padding: 0.625rem 0.75rem;
          border: 1px solid var(--mfs-border);
          border-radius: 0.625rem;
          background: var(--mfs-bg);
          color: var(--mfs-fg);
          cursor: pointer;
          font-weight: 600;
          list-style: none;
          transition: border-color 150ms ease, box-shadow 150ms ease;
        }

        .mint-flavor-switcher summary::-webkit-details-marker {
          display: none;
        }

        .mint-flavor-switcher summary:hover {
          border-color: var(--mfs-accent);
        }

        .mint-flavor-switcher summary:focus-visible {
          outline: 2px solid var(--mfs-accent);
          outline-offset: 2px;
        }

        .mint-flavor-switcher__label {
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
        }

        .mint-flavor-switcher__chevron {
          flex: none;
          transition: transform 150ms ease;
        }

        .mint-flavor-switcher details[open] .mint-flavor-switcher__chevron {
          transform: rotate(180deg);
        }

        .mint-flavor-switcher__menu {
          position: absolute;
          z-index: 50;
          top: calc(100% + 0.375rem);
          left: 0;
          width: 100%;
          box-sizing: border-box;
          margin: 0;
          padding: 0.375rem;
          border: 1px solid var(--mfs-border);
          border-radius: 0.625rem;
          background: var(--mfs-bg);
          box-shadow: 0 12px 30px rgb(0 0 0 / 16%);
          list-style: none;
        }

        .mint-flavor-switcher__menu li {
          margin: 0;
          padding: 0;
        }

        .mint-flavor-switcher__option {
          display: grid;
          gap: 0.125rem;
          padding: 0.625rem 0.75rem;
          border-radius: 0.4rem;
          color: var(--mfs-fg);
          text-decoration: none;
        }

        .mint-flavor-switcher__option:hover {
          background: var(--mfs-bg-hover);
        }

        .mint-flavor-switcher__option:focus-visible {
          outline: 2px solid var(--mfs-accent);
          outline-offset: -2px;
        }

        .mint-flavor-switcher__option[aria-current="page"] {
          background: var(--mfs-bg-current);
          color: var(--mfs-accent);
        }

        .mint-flavor-switcher__name {
          font-weight: 600;
        }

        .mint-flavor-switcher__description {
          color: var(--mfs-muted);
          font-size: 0.8125rem;
        }

        @media (prefers-reduced-motion: reduce) {
          .mint-flavor-switcher summary,
          .mint-flavor-switcher__chevron {
            transition: none;
          }
        }
      `}</style>

      <details>
        <summary aria-label={`${label}: ${selected.label}`}>
          <span className="mint-flavor-switcher__label">{selected.label}</span>
          <svg className="mint-flavor-switcher__chevron" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
            <path d="m6 9 6 6 6-6" />
          </svg>
        </summary>

        <ul className="mint-flavor-switcher__menu" aria-label={label}>
          {options.map(option => {
    const isCurrent = option.value === selected.value;
    const href = option.href ?? `${baseHref.replace(/\/$/, "")}/${encodeURIComponent(option.value)}`;
    return <li key={option.value}>
                <a className="mint-flavor-switcher__option" href={href} aria-current={isCurrent ? "page" : undefined}>
                  <span className="mint-flavor-switcher__name">
                    {option.label}
                  </span>
                  {option.description ? <span className="mint-flavor-switcher__description">
                      {option.description}
                    </span> : null}
                </a>
              </li>;
  })}
        </ul>
      </details>
    </div>;
};

<div className="mint-flavor-switcher-slot not-prose">
  <FlavorSwitcher
    current="react"
    baseHref="/doc/guides/building-search-ui/going-further/native"
    options={[
{ value: "js", label: "JavaScript", description: "InstantSearch.js" },
{ value: "react", label: "React", description: "React InstantSearch" },
{ value: "vue", label: "Vue", description: "Vue InstantSearch" },
]}
  />
</div>

<Note>
  This is the **React InstantSearch v7** documentation.
  If you're upgrading from v6, see the [upgrade guide](/doc/guides/building-search-ui/upgrade-guides/react/#migrate-from-react-instantsearch-v6-to-react-instantsearch-v7).
  If you were using React InstantSearch Hooks,
  this v7 documentation applies—just check for [necessary changes](/doc/guides/building-search-ui/upgrade-guides/react/#migrate-from-react-instantsearch-hooks-to-react-instantsearch-v7).
  To continue using v6, you can find the [archived documentation](https://algolia.com/old-docs/deprecated/instantsearch/react/v6/api-reference/instantsearch/).
</Note>

**React InstantSearch is compatible with [React Native](https://reactnative.dev/)** but,
since its UI components are designed for the web, they don't work directly in React Native apps.
You can use [Hooks](/doc/guides/building-search-ui/what-is-instantsearch/react#use-hooks)
with the React Native [core components](https://reactnative.dev/docs/intro-react-native-components)
or any third-party React Native component library to incorporate InstantSearch features into your React Native app.

This guide covers how to integrate InstantSearch in your React Native app:

* Adding a search box to send queries

* Displaying infinite hits and highlighting matches

* Filtering in a modal to narrow down the results set

* [View source code](https://github.com/algolia/doc-code-samples/tree/master/react-instantsearch-native/getting-started)

## Before you begin

This tutorial assumes you have React and React Native knowledge and an existing React Native app with React ≥ [16.8.0](https://reactjs.org/blog/2019/02/06/react-v16.8.0.html) app.
If you don't have a React Native app, [create one](https://reactnative.dev/docs/environment-setup).

The tutorial also requires an [installation of `algoliasearch` and `react-instantsearch-core`](/doc/guides/building-search-ui/installation/react).

## Add InstantSearch to your app

The [`InstantSearch`](/doc/api-reference/widgets/instantsearch/react) wrapper connects your InstantSearch app to your Algolia <Application />.

```js App.js icon=code theme={"system"}
import React from "react";
import { SafeAreaView, StatusBar, StyleSheet, Text, View } from "react-native";
import { liteClient as algoliasearch } from "algoliasearch/lite";
import { InstantSearch } from "react-instantsearch-core";

const searchClient = algoliasearch("ALGOLIA_APPLICATION_ID", "ALGOLIA_SEARCH_API_KEY");

export default function App() {
  return (
    <SafeAreaView style={styles.safe}>
      <StatusBar style="light" />
      <View style={styles.container}>
        <InstantSearch searchClient={searchClient} indexName="INDEX_NAME">
          {/* ... */}
        </InstantSearch>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  safe: {
    flex: 1,
    backgroundColor: "#252b33",
  },
  container: {
    flex: 1,
    backgroundColor: "#ffffff",
    flexDirection: "column",
  },
});
```

Replace `INDEX_NAME` with the name of your <Index />.
Add styles using the [`StyleSheet`](https://reactnative.dev/docs/stylesheet) API from React Native.

## Add a search box

The main UI component of a search experience is a search box.
It's often how users discover content in your app.

React InstantSearch provides a [`useSearchBox`](/doc/api-reference/widgets/search-box/react#hook) Hook to display an Algolia search box.
Use it with the [`TextInput`](https://reactnative.dev/docs/textinput) React Native component.
Then, add the custom `SearchBox` component to your app.

<CodeGroup>
  ```jsx SearchBox.js theme={"system"}
  import React, { useRef, useState } from "react";
  import { StyleSheet, View, TextInput } from "react-native";
  import { useSearchBox } from "react-instantsearch-core";

  export function SearchBox(props) {
    const { query, refine } = useSearchBox(props);
    const [inputValue, setInputValue] = useState(query);
    const inputRef = useRef(null);

    function setQuery(newQuery) {
      setInputValue(newQuery);
      refine(newQuery);
    }

    // Track when the InstantSearch query changes to synchronize it with
    // the React state.
    // Bypass the state update if the input is focused to avoid concurrent
    // updates when typing.
    if (query !== inputValue && !inputRef.current?.isFocused()) {
      setInputValue(query);
    }

    return (
      <View style={styles.container}>
        <TextInput
          ref={inputRef}
          style={styles.input}
          value={inputValue}
          onChangeText={setQuery}
          clearButtonMode="while-editing"
          autoCapitalize="none"
          autoCorrect={false}
          spellCheck={false}
          autoComplete="off"
        />
      </View>
    );
  }

  const styles = StyleSheet.create({
    container: {
      backgroundColor: "#252b33",
      padding: 18,
    },
    input: {
      height: 48,
      padding: 12,
      fontSize: 16,
      backgroundColor: "#fff",
      borderRadius: 4,
      borderWidth: 1,
      borderColor: "#ddd",
    },
  });
  ```

  ```jsx App.js theme={"system"}
  // ...
  import { SearchBox } from "./SearchBox";

  // ...

  export default function App() {
    return (
      <SafeAreaView style={styles.safe}>
        <StatusBar style="light" />
        <View style={styles.container}>
          <InstantSearch searchClient={searchClient} indexName="INDEX_NAME">
            <SearchBox />
          </InstantSearch>
        </View>
      </SafeAreaView>
    );
  }

  // ...
  ```
</CodeGroup>

Replace `INDEX_NAME` with the name of your index.

## Display infinite hits

When Algolia returns results, you want to list them in the UI.
A common way of dealing with lists on mobile devices is to display an "infinite list":
a list that loads more results when users scroll to the bottom of the screen.

To do this, use the [`useInfiniteHits`](/doc/api-reference/widgets/infinite-hits/react#hook) Hook with the [`FlatList`](https://reactnative.dev/docs/flatlist) React Native component.
Then, add the custom `InfiniteHits` component in your app and pass it a custom `Hit` component to display each Algolia result.

<CodeGroup>
  ```jsx InfiniteHits.js theme={"system"}
  import React from "react";
  import { StyleSheet, View, FlatList } from "react-native";
  import { useInfiniteHits } from "react-instantsearch-core";

  export function InfiniteHits({ hitComponent: Hit, ...props }) {
    const { items, isLastPage, showMore } = useInfiniteHits({
      ...props,
      escapeHTML: false,
    });

    return (
      <FlatList
        data={items}
        keyExtractor={(item) => item.objectID}
        ItemSeparatorComponent={() => <View style={styles.separator} />}
        onEndReached={() => {
          if (!isLastPage) {
            showMore();
          }
        }}
        renderItem={({ item }) => (
          <View style={styles.item}>
            <Hit hit={item} />
          </View>
        )}
      />
    );
  }

  const styles = StyleSheet.create({
    separator: {
      borderBottomWidth: 1,
      borderColor: "#ddd",
    },
    item: {
      padding: 18,
    },
  });
  ```

  ```jsx App.js theme={"system"}
  // ...
  import { InfiniteHits } from "./InfiniteHits";

  // ...

  export default function App() {
    return (
      <SafeAreaView style={styles.safe}>
        <StatusBar style="light" />
        <View style={styles.container}>
          <InstantSearch searchClient={searchClient} indexName="INDEX_NAME">
            <SearchBox />
            <InfiniteHits hitComponent={Hit} />
          </InstantSearch>
        </View>
      </SafeAreaView>
    );
  }

  function Hit({ hit }) {
    return <Text>{hit.name}</Text>;
  }

  // ...
  ```
</CodeGroup>

Replace `INDEX_NAME` with the name of your index.

<Frame title="Load more results as users scroll">
  <img src="https://mintcdn.com/algolia/1WCtS02LoYjihIhK/images/instantsearch/react/native/infinite-hits.gif?s=1a48a680bb855d1883953343a3fed26c" alt="Screenshot of a mobile interface displaying a list of products with 'Load more results as users scroll' feature." width="360" height="778" data-path="images/instantsearch/react/native/infinite-hits.gif" />
</Frame>

The search box remains visible, so users can update their <SearchQuery /> without manually scrolling back up.
When users do this, reset the scroll position of the list so they can see the top results first.

To do this:

1. Assign a reference (`ref`) to the list for later use in your app.
2. Wrap the `InfiniteHits` component in [`forwardRef`](https://reactjs.org/docs/forwarding-refs.html). This lets you pass `ref` from the app to the list.
3. Listen for the query to change by waiting for an `onChange` callback on the custom `SearchBox`.
4. When the query changes, call [`FlatList.scrollToOffset`](https://reactnative.dev/docs/flatlist#scrolltooffset) to scroll the list to the correct position.

<CodeGroup>
  ```jsx InfiniteHits.js theme={"system"}
  import React, { forwardRef } from "react";
  // ...

  export const InfiniteHits = forwardRef(
    ({ hitComponent: Hit, ...props }, ref) => {
      // ...

      return (
        <FlatList
          ref={ref}
          // ...
        />
      );
    },
  );

  // ...
  ```

  ```jsx SearchBox.js theme={"system"}
  // ...

  export function SearchBox({ onChange, ...props }) {
    // ...

    return (
      <View style={styles.container}>
        <TextInput
          // ...
          onChangeText={(newValue) => {
            setQuery(newValue);
            onChange(newValue);
          }}
        />
      </View>
    );
  }

  // ...
  ```

  ```jsx App.js theme={"system"}
  import React, { useRef } from "react";
  import {
    FlatList,
    SafeAreaView,
    StatusBar,
    StyleSheet,
    Text,
    View,
  } from "react-native";

  // ...

  export default function App() {
    const listRef = useRef < FlatList > null;

    function scrollToTop() {
      listRef.current?.scrollToOffset({ animated: false, offset: 0 });
    }

    return (
      <SafeAreaView style={styles.safe}>
        <StatusBar style="light" />
        <View style={styles.container}>
          <InstantSearch searchClient={searchClient} indexName="INDEX_NAME">
            <SearchBox onChange={scrollToTop} />
            <InfiniteHits ref={listRef} hitComponent={Hit} />
          </InstantSearch>
        </View>
      </SafeAreaView>
    );
  }

  // ...
  ```
</CodeGroup>

## Highlight matches

Algolia supports highlighting and returns the highlighted parts of a result in the response.
Build a custom `Highlight` component to highlight matches in each attribute.

The `instantsearch.js` library provides the necessary utilities to parse the highlighted parts from the Algolia response.
React InstantSearch uses InstantSearch.js behind the scenes, so you don't need to include it as a separate dependency.

Use the [`Text`](https://reactnative.dev/docs/text) component from React Native to represent each highlighted and non-highlighted part.
Then, use the custom `Highlight` component in your custom `Hit` component.

<CodeGroup>
  ```js Highlight.js theme={"system"}
  import React, { Fragment } from "react";
  import { StyleSheet, Text } from "react-native";
  import {
    getHighlightedParts,
    getPropertyByPath,
  } from "instantsearch.js/es/lib/utils";

  function HighlightPart({ children, isHighlighted }) {
    return (
      <Text style={isHighlighted ? styles.highlighted : styles.nonHighlighted}>
        {children}
      </Text>
    );
  }

  export function Highlight({ hit, attribute, separator = ", " }) {
    const { value: attributeValue = "" } =
      getPropertyByPath(hit._highlightResult, attribute) || {};
    const parts = getHighlightedParts(attributeValue);

    return (
      <>
        {parts.map((part, partIndex) => {
          if (Array.isArray(part)) {
            const isLastPart = partIndex === parts.length - 1;

            return (
              <Fragment key={partIndex}>
                {part.map((subPart, subPartIndex) => (
                  <HighlightPart
                    key={subPartIndex}
                    isHighlighted={subPart.isHighlighted}
                  >
                    {subPart.value}
                  </HighlightPart>
                ))}

                {!isLastPart && separator}
              </Fragment>
            );
          }

          return (
            <HighlightPart key={partIndex} isHighlighted={part.isHighlighted}>
              {part.value}
            </HighlightPart>
          );
        })}
      </>
    );
  }

  const styles = StyleSheet.create({
    highlighted: {
      fontWeight: "bold",
      backgroundColor: "#f5df4d",
      color: "#6f6106",
    },
    nonHighlighted: {
      fontWeight: "normal",
      backgroundColor: "transparent",
      color: "black",
    },
  });
  ```

  ```js App.js theme={"system"}
  // ...
  import { Configure, InstantSearch } from "react-instantsearch-core";
  import { Highlight } from "./Highlight";

  export default function App() {
    // ...

    return (
      <SafeAreaView style={styles.safe}>
        <StatusBar style="light" />
        <View style={styles.container}>
          <InstantSearch searchClient={searchClient} indexName="INDEX_NAME">
            <Configure highlightPreTag="<mark>" highlightPostTag="</mark>" />
            <SearchBox onChange={scrollToTop} />
            <InfiniteHits ref={listRef} hitComponent={Hit} />
          </InstantSearch>
        </View>
      </SafeAreaView>
    );
  }

  // ...

  function Hit({ hit }) {
    return (
      <Text>
        <Highlight hit={hit} attribute="name" />
      </Text>
    );
  }

  // ...
  ```
</CodeGroup>

Replace `INDEX_NAME` with the name of your index.

When users type a query, the UI highlights matches in each search result.

<img src="https://mintcdn.com/algolia/1WCtS02LoYjihIhK/images/instantsearch/react/native/highlight.jpg?fit=max&auto=format&n=1WCtS02LoYjihIhK&q=85&s=9a999f4a294ee35db79184a7ae223627" alt="Screenshot of a search results page for 'apple', with matching terms highlighted in yellow in product listings." width="1284" height="2778" data-path="images/instantsearch/react/native/highlight.jpg" />

## Add a filter

A search box is a great way to refine a search experience,
but sometimes users need to narrow down the results to find what they're looking for in a specific category.
Use the [`useRefinementList`](/doc/api-reference/widgets/refinement-list/react#hook) Hook to <Filter /> items based on attributes such as brand,
size, or color.

Mobile devices have limited screen real estate.
Instead of displaying filters near the list of hits as you would on a desktop website,
mobile apps commonly set filters inside a modal.
With React Native, use the [`Modal`](https://reactnative.dev/docs/modal) component.

<CodeGroup>
  ```js Filters.js theme={"system"}
  import React from "react";
  import {
    Button,
    StyleSheet,
    SafeAreaView,
    Modal,
    Text,
    TouchableOpacity,
    View,
  } from "react-native";
  import {
    useClearRefinements,
    useCurrentRefinements,
    useRefinementList,
  } from "react-instantsearch-core";

  export function Filters({ isModalOpen, onToggleModal, onChange }) {
    const { items, refine } = useRefinementList({ attribute: "brand" });
    const { canRefine: canClear, refine: clear } = useClearRefinements();
    const { items: currentRefinements } = useCurrentRefinements();
    const totalRefinements = currentRefinements.reduce(
      (acc, { refinements }) => acc + refinements.length,
      0,
    );

    return (
      <>
        <TouchableOpacity style={styles.filtersButton} onPress={onToggleModal}>
          <Text style={styles.filtersButtonText}>Filters</Text>
          {totalRefinements > 0 && (
            <View style={styles.itemCount}>
              <Text style={styles.itemCountText}>{totalRefinements}</Text>
            </View>
          )}
        </TouchableOpacity>

        <Modal animationType="slide" visible={isModalOpen}>
          <SafeAreaView>
            <View style={styles.container}>
              <View style={styles.title}>
                <Text style={styles.titleText}>Brand</Text>
              </View>
              <View style={styles.list}>
                {items.map((item) => {
                  return (
                    <TouchableOpacity
                      key={item.value}
                      style={styles.item}
                      onPress={() => {
                        refine(item.value);
                        onChange();
                      }}
                    >
                      <Text
                        style={{
                          ...styles.labelText,
                          fontWeight: item.isRefined ? "800" : "400",
                        }}
                      >
                        {item.label}
                      </Text>
                      <View style={styles.itemCount}>
                        <Text style={styles.itemCountText}>{item.count}</Text>
                      </View>
                    </TouchableOpacity>
                  );
                })}
              </View>
            </View>
            <View style={styles.filterListButtonContainer}>
              <View style={styles.filterListButton}>
                <Button
                  title="Clear all"
                  color="#252b33"
                  disabled={!canClear}
                  onPress={() => {
                    clear();
                    onChange();
                    onToggleModal();
                  }}
                />
              </View>
              <View style={styles.filterListButton}>
                <Button
                  onPress={onToggleModal}
                  title="See results"
                  color="#252b33"
                />
              </View>
            </View>
          </SafeAreaView>
        </Modal>
      </>
    );
  }

  const styles = StyleSheet.create({
    container: {
      padding: 18,
      backgroundColor: "#ffffff",
    },
    title: {
      alignItems: "center",
    },
    titleText: {
      fontSize: 32,
    },
    list: {
      marginTop: 32,
    },
    item: {
      paddingVertical: 12,
      flexDirection: "row",
      justifyContent: "space-between",
      borderBottomWidth: 1,
      borderColor: "#ddd",
      alignItems: "center",
    },
    itemCount: {
      backgroundColor: "#252b33",
      borderRadius: 24,
      paddingVertical: 4,
      paddingHorizontal: 8,
      marginLeft: 4,
    },
    itemCountText: {
      color: "#ffffff",
      fontWeight: "800",
    },
    labelText: {
      fontSize: 16,
    },
    filterListButtonContainer: {
      flexDirection: "row",
    },
    filterListButton: {
      flex: 1,
      alignItems: "center",
      marginTop: 18,
    },
    filtersButton: {
      paddingVertical: 18,
      flexDirection: "row",
      justifyContent: "center",
      alignItems: "center",
    },
    filtersButtonText: {
      fontSize: 18,
      textAlign: "center",
    },
  });
  ```

  ```js App.js theme={"system"}
  import React, { useRef, useState } from "react";
  import { Filters } from "./Filters";

  // ...

  export default function App() {
    const [isModalOpen, setModalOpen] = useState(false);

    // ...

    return (
      <SafeAreaView style={styles.safe}>
        <StatusBar style="light" />
        <View style={styles.container}>
          <InstantSearch searchClient={searchClient} indexName="INDEX_NAME">
            <SearchBox onChange={scrollToTop} />
            <Filters
              isModalOpen={isModalOpen}
              onToggleModal={() => setModalOpen((isOpen) => !isOpen)}
              onChange={scrollToTop}
            />
            <InfiniteHits ref={listRef} hitComponent={Hit} />
          </InstantSearch>
        </View>
      </SafeAreaView>
    );
  }

  // ...
  ```
</CodeGroup>

Replace `INDEX_NAME` with the name of your index.
Replace the `brand` attributes with one of your index's [faceting attributes](/doc/guides/managing-results/refine-results/faceting/how-to/declaring-attributes-for-faceting-with-dashboard).

<img src="https://mintcdn.com/algolia/1WCtS02LoYjihIhK/images/instantsearch/react/native/filters-modal.gif?s=9c949e9d7e1dc37feca02deca227adad" alt="Screenshot of a mobile app filter list with 'phone' entered in the search box, highlighting 'Phone' in several product listings." width="360" height="778" data-path="images/instantsearch/react/native/filters-modal.gif" />

The **Filters** button reveals filters in a modal that slides from the bottom.
It also uses the [`useCurrentRefinements`](/doc/api-reference/widgets/current-refinements/react#hook) and [`useClearRefinements`](/doc/api-reference/widgets/clear-refinements/react#hook) Hooks to display how many refinements are applied and let users clear them all at once.

## Next steps

With this tutorial, you've got a solid starting point for building dynamic search interfaces.
Some steps to improve the app are:

* [Search in multiple indices](/doc/guides/building-search-ui/ui-and-ux-patterns/multi-index-search/react) with [`Index`](/doc/api-reference/widgets/index-widget/react).
* Use a different component library like [Lottie React Native](https://github.com/lottie-react-native/lottie-react-native) or [React Native Elements](https://reactnativeelements.com/).
* Discover more Hooks by checking the [API reference](/doc/api-reference/widgets/react).
