React InstantSearch provides a comprehensive set of components to build search interfaces.
InstantSearch Wrapper
The <InstantSearch> component is the root provider that manages search state:
import { InstantSearch } from 'react-instantsearch';
import type { UiState } from 'instantsearch.js';
<InstantSearch<MyUiState>
searchClient={searchClient}
indexName="products"
insights={true}
routing={true}
stalledSearchDelay={300}
future={{
preserveSharedStateOnUnmount: true,
}}
onStateChange={({ uiState, setUiState }) => {
console.log('State changed:', uiState);
setUiState(uiState);
}}
>
{/* Your components */}
</InstantSearch>
Props
Algolia search client created with algoliasearch()
The name of the main index to search
Enable Algolia Insights for click and conversion tracking
routing
boolean | object
default:"false"
Enable URL synchronization. Can be true for defaults or an object with custom router and state mapping
Time in ms before a search is considered stalled
Opt into future default behaviors
Callback when search state changes
Search Components
SearchBox
A search input with submit and reset buttons:
import { SearchBox } from 'react-instantsearch';
<SearchBox
placeholder="Search products..."
searchAsYouType={true}
autoFocus={true}
ignoreCompositionEvents={false}
translations={{
submitButtonTitle: 'Submit search',
resetButtonTitle: 'Clear search',
}}
classNames={{
root: 'my-SearchBox',
form: 'my-SearchBox-form',
input: 'my-SearchBox-input',
}}
/>
Key Props:
searchAsYouType (default: true) - Search as user types vs. on submit only
queryHook - Modify query before search (e.g., for debouncing)
ignoreCompositionEvents - Useful for IME input
Hits
Display search results:
import { Hits } from 'react-instantsearch';
import type { Hit } from 'instantsearch.js';
type ProductHit = Hit<{
name: string;
price: number;
image: string;
}>;
function HitComponent({ hit, sendEvent }: { hit: ProductHit; sendEvent: Function }) {
return (
<article
onClick={() => {
sendEvent('click', hit, 'Product Clicked');
}}
>
<img src={hit.image} alt={hit.name} />
<h3>{hit.name}</h3>
<span>${hit.price}</span>
</article>
);
}
<Hits
hitComponent={HitComponent}
transformItems={(items) =>
items.map(item => ({
...item,
price: item.price / 100, // Convert cents to dollars
}))
}
classNames={{
root: 'my-Hits',
list: 'my-Hits-list',
item: 'my-Hits-item',
}}
/>
sendEvent Parameter:
The sendEvent function enables Insights tracking:
sendEvent('click', hit, eventName) - Track clicks
sendEvent('conversion', hit, eventName) - Track conversions
Highlight
Highlight matching parts of attributes:
import { Highlight } from 'react-instantsearch';
<Highlight
attribute="name"
hit={hit}
highlightedTagName="mark"
classNames={{
root: 'my-Highlight',
highlighted: 'my-Highlight-highlighted',
}}
/>
RefinementList
Faceted search filters:
import { RefinementList } from 'react-instantsearch';
<RefinementList
attribute="brand"
searchable={true}
searchablePlaceholder="Search brands..."
showMore={true}
showMoreLimit={20}
limit={10}
operator="or"
sortBy={['name:asc']}
transformItems={(items) =>
items.map(item => ({
...item,
label: item.label.toUpperCase(),
}))
}
translations={{
showMoreButtonText: ({ isShowingMore }) =>
isShowingMore ? 'Show less' : 'Show more',
noResultsText: 'No brands found',
}}
/>
Key Props:
attribute - Index attribute to filter on
operator - "or" (default) or "and" for combining values
searchable - Add search within facets
showMore - Enable “Show more” button
Navigate between result pages:
import { Pagination } from 'react-instantsearch';
<Pagination
totalPages={10}
padding={2}
showFirst={true}
showPrevious={true}
showNext={true}
showLast={true}
translations={{
firstPageItemText: 'First',
previousPageItemText: 'Previous',
nextPageItemText: 'Next',
lastPageItemText: 'Last',
pageItemText: ({ currentPage, nbPages }) => `${currentPage} of ${nbPages}`,
}}
/>
Set search parameters without UI:
import { Configure } from 'react-instantsearch';
<Configure
hitsPerPage={20}
attributesToSnippet={['description:50']}
snippetEllipsisText="..."
removeWordsIfNoResults="allOptional"
distinct={1}
enablePersonalization={true}
/>
Accepts any search parameter from the Algolia API.
Filtering Components
Multi-level categorization:
import { HierarchicalMenu } from 'react-instantsearch';
<HierarchicalMenu
attributes={[
'categories.lvl0',
'categories.lvl1',
'categories.lvl2',
]}
separator=" > "
showMore={true}
limit={10}
/>
Single-select filter:
import { Menu } from 'react-instantsearch';
<Menu
attribute="category"
showMore={true}
limit={5}
showMoreLimit={15}
sortBy={['name:asc']}
/>
Numeric range filter:
import { RangeInput } from 'react-instantsearch';
<RangeInput
attribute="price"
min={0}
max={1000}
precision={2}
translations={{
separatorElementText: 'to',
submitButtonText: 'Apply',
}}
/>
ToggleRefinement
Boolean filter:
import { ToggleRefinement } from 'react-instantsearch';
<ToggleRefinement
attribute="free_shipping"
label="Free shipping"
on={true}
off={false}
/>
CurrentRefinements
Display active filters:
import { CurrentRefinements } from 'react-instantsearch';
<CurrentRefinements
excludedAttributes={['query']}
transformItems={(items) =>
items.filter(item => item.attribute !== 'category')
}
/>
ClearRefinements
Clear all filters:
import { ClearRefinements } from 'react-instantsearch';
<ClearRefinements
excludedAttributes={['category']}
translations={{
resetButtonText: 'Clear all filters',
}}
/>
Utility Components
Stats
Display search statistics:
import { Stats } from 'react-instantsearch';
<Stats
translations={{
rootElementText({ nbHits, processingTimeMS }) {
return `${nbHits.toLocaleString()} results found in ${processingTimeMS}ms`;
},
}}
/>
SortBy
Switch between index replicas:
import { SortBy } from 'react-instantsearch';
<SortBy
items={[
{ label: 'Relevance', value: 'products' },
{ label: 'Price (asc)', value: 'products_price_asc' },
{ label: 'Price (desc)', value: 'products_price_desc' },
]}
/>
Index
Query multiple indices:
import { Index, Hits } from 'react-instantsearch';
<InstantSearch searchClient={searchClient} indexName="products">
<SearchBox />
{/* Main index */}
<Hits />
{/* Secondary index */}
<Index indexName="categories">
<Hits hitComponent={CategoryHit} />
</Index>
</InstantSearch>
Advanced Patterns
Conditional Rendering
Show/hide components based on state:
import { useInstantSearch } from 'react-instantsearch';
function ConditionalWidget() {
const { results } = useInstantSearch();
if (!results || results.nbHits === 0) {
return <p>No results found</p>;
}
return <Hits />;
}
Modify data before rendering:
<Hits
transformItems={(items) =>
items.map((item, index) => ({
...item,
position: index + 1,
discounted: item.price < item.originalPrice,
}))
}
hitComponent={HitComponent}
/>
Custom Class Names
All components accept a classNames prop for styling:
<SearchBox
classNames={{
root: 'search-box',
form: 'search-box__form',
input: 'search-box__input',
submit: 'search-box__submit',
reset: 'search-box__reset',
loadingIndicator: 'search-box__loading',
}}
/>
Next Steps
Hooks
Build fully custom components with hooks
Next.js Integration
Server-side rendering patterns