How to add AI-powered search to a React app

Build a React movie search and recommendation app with Meilisearch's AI-powered search.

Carolina Ferreira

Carolina Ferreira

Developer Advocate @ Meilisearch·@CarolainFG

·16 min read
How to add AI-powered search to a React app

Share the article

In this guide, we'll walk you through building an AI-powered movie recommendation app. Unlike traditional keyword search, AI-powered search uses machine learning to return results based on the context and meaning behind queries.

You'll build a search and recommendation system using Meilisearch and OpenAI's embedding model. The app will offer a search-as-you-type experience, combining precise keyword matching with the deeper context of semantic search, helping users find relevant movies even when their queries don't exactly match movie titles or descriptions.

Additionally, the app will feature AI-powered recommendations, suggesting similar movies based on the user’s selection, to enhance their experience.

Whether you're new to Meilisearch or expanding your search skills, this tutorial will guide you through building a cutting-edge movie search and recommendation system. Let's get started!

Prerequisites

Before we begin, make sure you have:

  • Node.js and npm (included with Node.js)
  • A running v1.10 Meilisearch project – a search engine to create out-of-the-box relevant search experiences
  • An API key from OpenAI to use their embedding models (at least a tier 2 key for optimal performance)

1. Set up Meilisearch

In this guide we'll use Meilisearch Cloud, as it's the easiest option get Meilisearch up and running fast. You can try it free for 14 days, no credit card required. It's also the recommended way to run Meilisearch in production environments.

If you prefer to run things on your own machine, no problem - Meilisearch is open-source, so you can install it locally.

1.1. Create a new index

Create an index called movies and add this movies.json to it. If necessary, follow the getting started guide.

Each document in the movies dataset represents a single movie and has the following structure:

  • id: a unique identifier for each movie
  • title: the title of the movie
  • overview: a brief summary of the movie's plot
  • genres: an array of genres that the movie belongs to
  • poster: a URL to the movie's poster image
  • release_date: the release date of the movie, represented as a Unix timestamp

In the Meilisearch Cloud dashboard:

  • Find the "Experimental features" section in your project settings
  • Check the "AI-powered search" box

AI-powered search checkbox checked

Alternatively, activate it via the API, using the experimental-features route.

1.3. Configure the embedder

To harness the power of AI-powered search, we need to configure an embedder for our index.

When we configure an embedder, we're telling Meilisearch how to convert our text data into embeddings–numerical representations of the text that capture its semantic meaning. This allows for semantic similarity comparisons, enabling our search to understand context and meaning beyond simple keyword matching.

We'll use OpenAI's model for this tutorial, but Meilisearch is compatible with various embedders. You can explore other options in our compatibility list. Don’t know which model to choose? We've got you covered, read our blog post on choosing the right model for semantic search.

Configure the embedder index setting:

  • In the Cloud UI:

Embedder configuration in Meilisearch Cloud UI

  • Or via API:
bash
  • text is the name we have given to our embedder
  • Replace https://ms-*****.sfo.meilisearch.io with your project's URL
  • ReplaceYOUR_MEILISEARCH_API_KEY and YOUR_OPENAI_API_KEY with your actual keys
  • The model field specifies the OpenAI model to use
  • The documentTemplate field customizes the data sent to the embedder

Tip: Create short, relevant document templates for better search results and optimal performance.

2. Create a React app

Now that our Meilisearch backend is configured, let's set up the frontend of our AI-powered search application using React.

2.1. Set up the project

We’ll use a Vite template to create a new React project with a basic structure, setting us up for rapid development.

json

2.2. Install the Meilisearch client

Next, we need to install the Meilisearch JavaScript client to interact with our Meilisearch backend:

json

2.3. Add Tailwind CSS

For styling, we'll use Tailwind CSS. Instead of installing it as a dependency, we'll use the Tailwind CSS Play CDN for simplicity. Add the following script tag to the <head> of your index.html file:

html

We've also updated the <title> tag to reflect our app's purpose.

2.4. Verify the setup

To ensure everything is set up correctly, start the development server:

json

You should see a URL (usually http://localhost:5173) where you can view your app in the browser. If you see the Vite + React welcome page, you're all set!

With these steps completed, we have a React project ready for building our AI-powered movie search interface. In the next sections, we'll start implementing the search functionality using Meilisearch.

3. Build an AI-powered search experience

Hybrid search combines traditional keyword search with AI-powered semantic search. Keyword search is great for precise matches, while semantic search understands context. By using both, we get the best of both worlds - precise results and contextually relevant matches.

3.1. Create a MovieSearchService.jsx file

We have a Meilisearch instance running, and to interact with it, we create a MovieSearchService.jsx file in our src directory. This service acts as a client-side interface to our Meilisearch backend, providing essential search-related functionality for our movie database.

First, we need to add the Meilisearch credentials to an .env file. You can find the Database URL (your host) and Default Search API Key on your Meilisearch Cloud project’s Settings page.

json

Note that variables in Vite projects must be prefixed with VITE_ to be accessible in the application code.

Now, let’s create the Meilisearch client to connect to the Meilisearch instance:

javascript

Next, let’s create a function to perform a hybrid search:

JavaScript

When adding the hybrid parameter to the search query, Meilisearch returns a mix of semantic and full-text matches.

The semanticRatio determines the balance between keyword and semantic search, with 1 being full semantic and 0 full keyword. A ratio of 0.5 means the results will be equally influenced by both methods. Adjusting this ratio allows you to fine-tune the search behavior to best suit your data and user needs.

The embedder specifies the configured embedder. Here, we are using the text embedder configured in step 1.3.

3.2. Create the search UI components

First, let's create a dedicated directory for our components src/components to keep our project clean and manageable as it grows.

3.2.1. The search input

Now, we can create our search input component. This will be the main interface for users to interact with our AI-powered search. Create a new file SearchInput.jsx in the src/components directory:

JavaScript

The SearchInput component takes two props: query and setQuery. The input field's value is controlled by the query prop. When the user types in the input, it triggers the onChange event, which calls setQuery with the new value.

A clear button (❌) appears when there's any text in the input (when query is truthy). Clicking this button sets the query to an empty string, effectively clearing the input.

We'll control the state and behavior of the query and setQuery props in the parent component App.jsx.

3.2.2. The result card

Now that we have a search bar, we need a component to display the search results. Let's create a ResultCard component to showcase each movie returned by our search.

Create a new file ResultCard.jsx in the src/components directory:

JavaScript

This component takes url, title, and overview as props. The component shows the movie poster using the url prop, followed by the title and a truncated overview, providing a compact preview of each movie.

3.3. Integrate search and UI in the main App component

Let's update the App.jsx component to tie everything together, handling the search logic and rendering the UI.

JavaScript

We use several state variables:

  • query: stores the current search query
  • results: holds the search results
  • isLoading: indicates when a search is in progress
  • error: stores any error messages

The core of the component is a useEffect hook that triggers a performSearch function whenever the query changes. This function manages the search process, including setting loading states, calling the hybridSearch function, updating results, and handling any errors.

In the render method, we structure our UI with the SearchInput component at the top, followed by loading and error messages when applicable. The search results are displayed as a grid of ResultCard components, mapping over the results array.

4. Build the movie recommendation system

Now that we've implemented the search logic, let's enhance our application with a recommendation system. Meilisearch offers an AI-powered similarity search feature through its /similar route. This feature allows us to retrieve a number of documents that are similar to a target document, which is perfect for creating movie recommendations.

Let's add this functionality to our MovieSearchService.jsx:

JavaScript

The searchSimilarDocuments index method takes the id of the target movie and the embedder name as parameters. It can also can be used together with other search parameters such as limit to control the number of recommendations.

4.1. Create a modal to display the recommendations

Let’s create a modal to display movie details and recommendations. A modal allows us to show more information without navigating away from the search results, which improves user experience by maintaining context.

JavaScript

This component takes 3 props:

  • movie: an object containing details of the selected movie. This prop is used to display the main content of the modal.
  • similarMovies: an array of movie objects representing films similar to the main movie. We reuse our ResultCard component to showcase each recommended movie.
  • onClose: a function that is called when the modal should be closed. This function is triggered when the close button is clicked or when the 'Escape' key is pressed.

The useRef and useEffect hooks are used to manage focus and keyboard interactions, which are crucial for accessibility. The aria-* attributes further enhance the modal's accessibility for screen readers.

4.2. Implement modal functionality

Let’s update the main App.jsx component so we can call the similar movies function and open the modal when we click on a movie.

First let’s import the modal and the searchSimilarMovies function we created earlier:

JavaScript

Add state for selectedMovie using useState:

JavaScript

This creates a state variable to store the currently selected movie, initially set to null, and a function to update it.

Next, let’s create 2 functions:

  • handleMovieClick to update the selectedMovie state with the movie that was clicked, enabling the modal to display the selected movie's details
  • closeModal to reset the selectedMovie state to null
JavaScript

Now, we can update the ResultCard component to trigger the handleMovieClick function when clicked and add the MovieModal component to the JSX, conditionally rendering it when a movie is selected.

JavaScript
javascript

Let’s create a new state variable similarMovies (initially an empty array) and its setter function setSimilarMovies to store and update the list of movies similar to the selected one.

javascript

Now, we need to udpate the handleMovieClick function to also fetch similar movies, and update the similarMovies state with the results, which we will pass on to the modal.

JavaScript

Finally, we need to update the closeModal to reset similarMovies state variable:

JavaScript

5. Run the application

Start the development server and enjoy!

json

Our app should look like this:

Typing "ace ventura" in the search bar getting results with each keystroke, clicking on the movie cat of "Ace Ventura: pet detective" and a modal opens with the movie poster and the full overview and 3 similar movies: "Ace Ventura Jr: pet detective", "Ace Ventura: when the nature calls", and "The Animal".

Conclusion

Congratulations! You've successfully built an AI-powered movie search and recommendation system using Meilisearch and React. Let's recap what you've accomplished:

  1. Set up a Meilisearch project and configured it for AI-powered search
  2. Implemented hybrid search combining keyword and semantic search capabilities
  3. Created a React UI for searching movies
  4. Integrated Meilisearch's similarity search for movie recommendations

What's next?

To improve user experience and allow for more precise searching, you could set a faceted search interface to allow users filter movies by genre or sort them by release date.

When you're ready to build an app with your own data, make sure to configure your index settings first to follow best practices. This will optimize indexing performance and search relevancy.

When you're ready to build an app with your own data, make sure to configure your index settings first to follow best practices. This will optimize indexing performance and search relevancy, ensuring your app runs smoothly and provides accurate results.


Meilisearch is an open-source search engine with intuitive developer experience to build user-facing search. You can self-host it or get a premium experience with Meilisearch Cloud.

For more things Meilisearch, you can join the community on Discord or subscribe to the newsletter. You can learn more about the product by checking out the roadmap and participating in product discussions.

Carolina Ferreira

Carolina Ferreira

Developer Advocate @ Meilisearch

Carolina joined Meilisearch in 2020 as a Developer Advocate. With a background in translation and teaching, she discovered programming by chance and quickly became passionate about it. She has worked in DevRel and tech support and is now transitioning into a Solution Engineer role, enjoying the diverse challenges along the way. Outside of work, she loves staying active, music, cinema, traveling, and exploring new cuisines—one of her favorite parts of any trip.

Related articles