# Building a Search Bar in React Native

This guide walks you through building a native mobile search interface using React Native (Expo) and Typesense.

You'll create a cross-platform book search application that works on both iOS and Android devices, demonstrating how to integrate Typesense into your React Native projects.

Mobile users expect instant results and won't wait around for slow searches. That's why combining React Native's native performance with Typesense's lightning-fast search engine creates the perfect foundation for a mobile app search experience that your users will love.

# What is Typesense?

Typesense is a lightning-fast, typo-tolerant search engine that makes it easy to add powerful search to your applications. Think of it as your personal search assistant that understands what users are looking for, even when they make mistakes.

Here's a real-world scenario: imagine you're building a food delivery app like DoorDash or Uber Eats. A hungry user opens your app at midnight and searches for "piza hut" (with typos). Instead of showing "no restaurants found" and sending them to a competitor's app, Typesense instantly understands they meant "Pizza Hut" and shows nearby locations with delivery times. That split-second difference between a successful search and a frustrated user can make or break your app's retention. That's the magic of intelligent search!

Why developers choose Typesense:

  • Blazing fast - Search results appear in milliseconds, even across millions of documents.
  • Typo-tolerant - Automatically corrects spelling mistakes so users find what they need.
  • Feature-Rich - Full-text search, Synonyms, Curation Rules, Semantic Search, Hybrid search, Conversational Search (like ChatGPT for your data), RAG, Natural Language Search, Geo Search, Vector Search and much more wrapped in a single binary for a batteries-included developer experience.
  • Simple setup - Get started in minutes with Docker, no complex configuration needed like Elasticsearch.
  • Cost-effective - Self-host for free, unlike expensive alternatives like Algolia.
  • Open source - Full control over your search infrastructure, or use Typesense Cloud (opens new window) for hassle-free hosting.

# Prerequisites

This guide will use React Native (opens new window) with Expo (opens new window), a framework that makes it easy to build native mobile apps using JavaScript and React.

Please ensure you have the following installed on your machine before proceeding:

This guide will use a Linux environment, but you can adapt the commands to your operating system.

# Step 1: Setup your Typesense server

Once Docker is installed, you can run a Typesense container in the background using the following commands:

  • Create a folder that will store all searchable data stored for Typesense:

    mkdir "$(pwd)"/typesense-data
    
  • Run the Docker container:

  • Verify if your Docker container was created properly:

    docker ps
    
  • You should see the Typesense container running without any issues:

    CONTAINER ID   IMAGE                      COMMAND                  CREATED       STATUS       PORTS                                         NAMES
    82dd6bdfaf66   typesense/typesense:latest   "/opt/typesense-serv…"   1 min ago   Up 1 minutes   0.0.0.0:8108->8108/tcp, [::]:8108->8108/tcp   nostalgic_babbage
    
  • That's it! You are now ready to create collections and load data into your Typesense server.

TIP

You can also set up a managed Typesense cluster on Typesense Cloud (opens new window) for a fully managed experience with a management UI, high availability, globally distributed search nodes and more.

# Step 2: Create a new books collection and load sample dataset into Typesense

Typesense needs you to create a collection in order to search through documents. A collection is a named container that defines a schema and stores indexed documents for search. Collection bundles three things together:

  1. Schema
  2. Document
  3. Index

You can create the books collection for this project using this curl command:

curl "http://localhost:8108/collections" \
      -X POST \
      -H "Content-Type: application/json" \
      -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
      -d '{
            "name": "books",
            "fields": [
              {"name": "title", "type": "string", "facet": false},
              {"name": "authors", "type": "string[]", "facet": true},
              {"name": "publication_year", "type": "int32", "facet": true},
              {"name": "average_rating", "type": "float", "facet": true},
              {"name": "image_url", "type": "string", "facet": false},
              {"name": "ratings_count", "type": "int32", "facet": true}
            ],
            "default_sorting_field": "ratings_count"
          }'

Now that the collection is set up, we can load the sample dataset.

  1. Download the sample dataset:

    curl -O https://dl.typesense.org/datasets/books.jsonl.gz
    
  2. Unzip the dataset:

    gunzip books.jsonl.gz
    
  3. Load the dataset in to Typesense:

    curl "http://localhost:8108/collections/books/documents/import" \
          -X POST \
          -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
          --data-binary @books.jsonl
    

You should see a bunch of success messages if the data load is successful.

Now you're ready to actually build the application.

# Step 3: Set up your React Native project

Create a new Expo project using this command:

npx create-expo-app@latest typesense-react-native-search-bar --template blank-typescript

This will scaffold a new React Native project with TypeScript support using Expo.

Once your project scaffolding is ready, navigate to the project directory and install the required dependencies:

cd typesense-react-native-search-bar
npm install
npm i react-instantsearch-core typesense-instantsearch-adapter

Let's go over the key dependencies:

  • react-instantsearch-core - Provides InstantSearch (opens new window) hooks and components for React Native. InstantSearch is an open-source UI library by Algolia for building search interfaces that works with any search backend through adapters.
  • typesense-instantsearch-adapter - Connects InstantSearch with Typesense as the search engine

Note

React Native can use react-instantsearch-core with the typesense-instantsearch-adapter (opens new window), just like web frameworks. This gives you access to powerful InstantSearch hooks and widgets. Alternatively, you can make direct API calls to Typesense using the Fetch API for a lighter implementation.

# Project Structure

Let's create the project structure step by step. After each step, we'll show you how the directory structure evolves.

  1. After creating the basic Expo app and installing the required dependencies, your project structure should look like this:

    typesense-react-native-search-bar/
    ├── node_modules/
    ├── assets/
    │   ├── adaptive-icon.png
    │   ├── favicon.png
    │   ├── icon.png
    │   └── splash-icon.png
    ├── App.tsx
    ├── app.json
    ├── package-lock.json
    ├── package.json
    └── tsconfig.json
    
  2. Create the project directories:

    mkdir -p components types utils
    

    Your project structure should now look like this:

    typesense-react-native-search-bar/
    ├── assets/
    │   ├── adaptive-icon.png
    │   ├── favicon.png
    │   ├── icon.png
    │   └── splash-icon.png
    ├── components/
    ├── types/
    ├── utils/
    ├── App.tsx
    ├── app.json
    ├── package-lock.json
    ├── package.json
    └── tsconfig.json
    
  3. Create the environment configuration file .env:

    touch .env
    

    Add this content to .env:

    EXPO_PUBLIC_TYPESENSE_API_KEY=xyz
    EXPO_PUBLIC_TYPESENSE_HOST=10.0.2.2
    EXPO_PUBLIC_TYPESENSE_PORT=8108
    EXPO_PUBLIC_TYPESENSE_PROTOCOL=http
    

    Important

    • For Android emulator, use 10.0.2.2 as the host (this maps to localhost on your machine)
    • For iOS simulator, use localhost as the host
    • For physical devices, use your machine's local IP address (e.g., 192.168.1.100) or use a tunneling service like ngrok (opens new window)

    Expo automatically loads environment variables starting with EXPO_PUBLIC_ and makes them available to your app via process.env.

    Using env variables is NOT recommended for production apps since they are bundled with the app. Instead, you want to generate a search-only API key and serve it to your app from a backend endpoint, along with your Typesense cluster endpoints. This way you can rotate your API keys and change your cluster endpoints at any point as needed.

  4. Create the types file:

    touch types/Book.ts
    

    Add this to types/Book.ts:

    export interface Book {
      id: string;
      title: string;
      authors: string[];
      image_url: string;
      publication_year: number;
      average_rating?: number;
      ratings_count?: number;
    }
    

    Your project structure should now look like this:

    typesense-react-native-search-bar/
    ├── assets/
    ├── components/
    ├── types/
    │   └── Book.ts
    ├── utils/
    ├── .env
    ├── App.tsx
    ├── app.json
    ├── package.json
    └── tsconfig.json
    
  5. Create the Typesense utility file:

    touch utils/typesense.ts
    

    Add this to utils/typesense.ts:

    import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter";
    
    const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
      server: {
        apiKey: process.env.EXPO_PUBLIC_TYPESENSE_API_KEY || "xyz",
        nodes: [
          {
            host: process.env.EXPO_PUBLIC_TYPESENSE_HOST || "localhost",
            port: Number(process.env.EXPO_PUBLIC_TYPESENSE_PORT) || 8108,
            protocol: process.env.EXPO_PUBLIC_TYPESENSE_PROTOCOL || "http",
          },
        ],
      },
      additionalSearchParameters: {
        query_by: "title,authors",
      },
    });
    
    export const searchClient = typesenseInstantsearchAdapter.searchClient;
    

    This utility file creates the Typesense InstantSearch adapter, which bridges Typesense with InstantSearch. The adapter handles all the communication with Typesense and provides a search client that works seamlessly with react-instantsearch-core hooks.

  6. Create the component files:

    touch components/SearchInput.tsx
    touch components/BookCard.tsx
    touch components/BookList.tsx
    

    Your project structure should now look like this:

    typesense-react-native-search-bar/
    ├── assets/
    ├── components/
    │   ├── BookCard.tsx
    │   ├── BookList.tsx
    │   └── SearchInput.tsx
    ├── types/
    │   └── Book.ts
    ├── utils/
    │   └── typesense.ts
    ├── .env
    ├── App.tsx
    ├── app.json
    ├── package.json
    └── tsconfig.json
    
  7. Create the SearchInput component in components/SearchInput.tsx:

    import React from "react";
    import { StyleSheet, TextInput } from "react-native";
    import { useSearchBox } from "react-instantsearch-core";
    
    export const SearchInput = () => {
      const { query, refine } = useSearchBox();
    
      return (
        <TextInput
          style={styles.searchInput}
          placeholder="Search books..."
          placeholderTextColor="#999"
          value={query}
          onChangeText={refine}
        />
      );
    };
    
    const styles = StyleSheet.create({
      searchInput: {
        backgroundColor: "#444",
        color: "white",
        padding: 15,
        borderRadius: 8,
        marginHorizontal: 20,
        marginBottom: 20,
        marginTop: 0,
        fontSize: 16,
      },
    });
    

    This component uses the useSearchBox hook from react-instantsearch-core to connect the search input to Typesense. The query value and refine function are provided by InstantSearch, which handles debouncing and search state management automatically.

  8. Create the BookCard component in components/BookCard.tsx:

    import React from "react";
    import { View, Image, Text, StyleSheet, Dimensions } from "react-native";
    import { Book } from "../types/Book";
    
    export const BookCard = ({ book }: { book: Book }) => {
      return (
        <View key={book.id} style={styles.card}>
          <Image
            source={{ uri: book.image_url }}
            style={styles.bookImage}
            resizeMode="cover"
          />
          <View style={styles.cardContent}>
            <Text style={styles.title} numberOfLines={2}>
              {book.title}
            </Text>
            <Text style={styles.authors} numberOfLines={1}>
              {book.authors.join(", ")}
            </Text>
            <Text style={styles.year}>{book.publication_year}</Text>
          </View>
        </View>
      );
    };
    
    const styles = StyleSheet.create({
      card: {
        backgroundColor: "#333",
        borderRadius: 12,
        margin: 8,
        width: Dimensions.get("window").width / 2 - 24,
        shadowColor: "#000",
        shadowOffset: { width: 0, height: 2 },
        shadowOpacity: 0.3,
        shadowRadius: 4,
        elevation: 5,
      },
      bookImage: {
        width: "100%",
        height: 200,
        borderTopLeftRadius: 12,
        borderTopRightRadius: 12,
      },
      cardContent: {
        padding: 12,
      },
      title: {
        fontSize: 16,
        fontWeight: "bold",
        color: "white",
        marginBottom: 4,
      },
      authors: {
        fontSize: 14,
        color: "#ccc",
        marginBottom: 4,
      },
      year: {
        fontSize: 12,
        color: "#999",
      },
    });
    
  9. Create the BookList component in components/BookList.tsx:

    import React from "react";
    import { useHits } from "react-instantsearch-core";
    import { BookCard } from "./BookCard";
    import { Book } from "../types/Book";
    
    export const BookList = () => {
      const { hits } = useHits<Book>();
    
      return (
        <>
          {hits.map((book) => (
            <BookCard key={book.id} book={book} />
          ))}
        </>
      );
    };
    

    This component uses the useHits hook from react-instantsearch-core to automatically receive search results from Typesense. The hook provides the hits array which updates in real-time as the user types in the search box.

  10. Finally, update your App.tsx to bring everything together:

    import { StatusBar } from "expo-status-bar";
    import { StyleSheet, View, ScrollView } from "react-native";
    import {
      SafeAreaProvider,
      useSafeAreaInsets,
    } from "react-native-safe-area-context";
    import { InstantSearch } from "react-instantsearch-core";
    import { Heading } from "./components/Heading";
    import { BookList } from "./components/BookList";
    import { SearchInput } from "./components/SearchInput";
    import { searchClient } from "./utils/typesense";
    
    function AppContent() {
      const insets = useSafeAreaInsets();
    
      return (
        <View style={[styles.container, { paddingTop: insets.top }]}>
          <ScrollView>
            <Heading />
            <SearchInput />
            <View style={styles.grid}>
              <BookList />
            </View>
            <StatusBar style="auto" />
          </ScrollView>
        </View>
      );
    }
    
    export default function App() {
      return (
        <SafeAreaProvider>
          <InstantSearch searchClient={searchClient} indexName="books">
            <AppContent />
          </InstantSearch>
        </SafeAreaProvider>
      );
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        backgroundColor: "#222",
      },
      grid: {
        flexDirection: "row",
        flexWrap: "wrap",
        justifyContent: "center",
        paddingHorizontal: 8,
      },
    });
    

    This main component wraps the app with the InstantSearch provider, which connects all the search components to Typesense. The searchClient from our utility file and the indexName ("books") are passed to the provider. The SafeAreaProvider ensures the app respects device-specific safe areas like notches and home indicators.

  11. Your final project structure should look like this:

    typesense-react-native-search-bar/
    ├── assets/
    │   ├── adaptive-icon.png
    │   ├── favicon.png
    │   ├── icon.png
    │   └── splash-icon.png
    ├── components/
    │   ├── BookCard.tsx
    │   ├── BookList.tsx
    │   └── SearchInput.tsx
    ├── types/
    │   └── Book.ts
    ├── utils/
    │   └── typesense.ts
    ├── .env
    ├── App.tsx
    ├── app.json
    ├── package.json
    └── tsconfig.json
    
  12. Run the application:

    npx expo start
    

    This will start the Expo development server. You can then:

    • Scan the QR code with Expo Go app (Android) or Camera app (iOS).
    • Press a to open in Android emulator.
    • Press i to open in iOS simulator.
    • Press w to open in web browser.

You've successfully built a mobile search interface with React Native and Typesense!

# Final Output

Here's how the final output should look like on a mobile device:

React Native Search Bar Final Output

# Source Code

Here's the complete source code for this project on GitHub:

https://github.com/typesense/code-samples/tree/master/typesense-react-native-search-bar (opens new window)

Here are other related examples that show you how to build search interfaces with different frameworks:

# Need Help?

Read our Help section for information on how to get additional help.

Last Updated: 2/16/2026, 6:00:01 PM