How to Add Algolia Search to Your Website
How to Add Algolia Search to Your Website
Algolia delivers search results in under 50ms. This guide covers the full integration: indexing your data, building a search UI with InstantSearch, adding filters and facets, and tracking search analytics.
What You'll Build
- Full-text search with typo tolerance and highlighting
- Faceted filtering (category, price range, tags)
- Search-as-you-type with instant results
- Search analytics (what users search for, no-result queries)
Prerequisites: React 18+ or Next.js 14+, Algolia account (free: 10K searches/month).
1. Setup
Install
npm install algoliasearch react-instantsearch
Get Your Credentials
From the Algolia Dashboard:
- Application ID — identifies your app
- Search-Only API Key — safe for client-side (read-only)
- Admin API Key — for indexing (server-side only)
Environment Variables
# .env.local
NEXT_PUBLIC_ALGOLIA_APP_ID=YOUR_APP_ID
NEXT_PUBLIC_ALGOLIA_SEARCH_KEY=your_search_only_key
ALGOLIA_ADMIN_KEY=your_admin_key # Server-side only
2. Index Your Data
Push Data to Algolia
// scripts/index-data.ts
import algoliasearch from 'algoliasearch';
const client = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
process.env.ALGOLIA_ADMIN_KEY!
);
const index = client.initIndex('products');
const products = [
{
objectID: '1',
name: 'React Query',
description: 'Powerful data fetching for React',
category: 'Data Fetching',
stars: 38000,
tags: ['react', 'data', 'cache'],
},
{
objectID: '2',
name: 'Zustand',
description: 'Small, fast state management',
category: 'State Management',
stars: 42000,
tags: ['react', 'state', 'lightweight'],
},
// ... more records
];
// Push records to Algolia
await index.saveObjects(products);
console.log('Indexed', products.length, 'records');
Configure Index Settings
await index.setSettings({
// Which fields to search
searchableAttributes: ['name', 'description', 'tags'],
// Which fields to use as filters
attributesForFaceting: ['category', 'tags', 'filterOnly(stars)'],
// Custom ranking (Algolia handles relevance, you add business logic)
customRanking: ['desc(stars)'],
// Highlighting
highlightPreTag: '<mark>',
highlightPostTag: '</mark>',
});
Keep Data in Sync
For real-time sync, update Algolia when your database changes:
// When a product is created/updated
await index.saveObject({
objectID: product.id,
name: product.name,
description: product.description,
category: product.category,
stars: product.stars,
tags: product.tags,
});
// When a product is deleted
await index.deleteObject(product.id);
3. Basic Search UI
InstantSearch Provider
// components/SearchProvider.tsx
'use client';
import algoliasearch from 'algoliasearch/lite';
import { InstantSearch } from 'react-instantsearch';
const searchClient = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY!
);
export function SearchProvider({ children }: { children: React.ReactNode }) {
return (
<InstantSearch searchClient={searchClient} indexName="products">
{children}
</InstantSearch>
);
}
Search Box + Results
// components/Search.tsx
'use client';
import {
SearchBox,
Hits,
Highlight,
Stats,
Pagination,
} from 'react-instantsearch';
function HitComponent({ hit }: { hit: any }) {
return (
<article>
<h3>
<Highlight attribute="name" hit={hit} />
</h3>
<p>
<Highlight attribute="description" hit={hit} />
</p>
<span className="category">{hit.category}</span>
<span className="stars">⭐ {hit.stars.toLocaleString()}</span>
</article>
);
}
export function Search() {
return (
<div>
<SearchBox
placeholder="Search APIs..."
classNames={{
input: 'search-input',
submit: 'search-submit',
}}
/>
<Stats />
<Hits hitComponent={HitComponent} />
<Pagination />
</div>
);
}
Full Page
// app/search/page.tsx
import { SearchProvider } from '@/components/SearchProvider';
import { Search } from '@/components/Search';
export default function SearchPage() {
return (
<SearchProvider>
<h1>Search APIs</h1>
<Search />
</SearchProvider>
);
}
4. Faceted Filtering
Add sidebar filters for categories, tags, and numeric ranges:
// components/SearchWithFilters.tsx
'use client';
import {
SearchBox,
Hits,
RefinementList,
RangeInput,
ClearRefinements,
CurrentRefinements,
Highlight,
Pagination,
} from 'react-instantsearch';
export function SearchWithFilters() {
return (
<div className="search-layout">
<aside className="filters">
<ClearRefinements />
<CurrentRefinements />
<h3>Category</h3>
<RefinementList attribute="category" />
<h3>Tags</h3>
<RefinementList
attribute="tags"
limit={10}
showMore
searchable
searchablePlaceholder="Search tags..."
/>
<h3>Minimum Stars</h3>
<RangeInput attribute="stars" />
</aside>
<main className="results">
<SearchBox placeholder="Search..." />
<Hits hitComponent={HitComponent} />
<Pagination />
</main>
</div>
);
}
5. Autocomplete / Search-as-You-Type
For a dropdown autocomplete experience:
'use client';
import { useState, useEffect } from 'react';
import algoliasearch from 'algoliasearch/lite';
const searchClient = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY!
);
const index = searchClient.initIndex('products');
export function Autocomplete() {
const [query, setQuery] = useState('');
const [results, setResults] = useState<any[]>([]);
useEffect(() => {
if (query.length < 2) {
setResults([]);
return;
}
const search = async () => {
const { hits } = await index.search(query, {
hitsPerPage: 5,
attributesToRetrieve: ['name', 'category'],
});
setResults(hits);
};
const debounce = setTimeout(search, 200);
return () => clearTimeout(debounce);
}, [query]);
return (
<div className="autocomplete">
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search APIs..."
/>
{results.length > 0 && (
<ul className="suggestions">
{results.map((hit) => (
<li key={hit.objectID}>
<a href={`/api/${hit.objectID}`}>
{hit.name} <span className="category">{hit.category}</span>
</a>
</li>
))}
</ul>
)}
</div>
);
}
6. Search Analytics
Track what users search for and improve results:
Enable Analytics
await index.setSettings({
enableAnalytics: true,
});
Click Analytics
Track which results users click:
import { createInsightsMiddleware } from 'instantsearch.js/es/middlewares';
import { Hits, Configure } from 'react-instantsearch';
// In your InstantSearch component
<Configure clickAnalytics />
// In your hit component, send click events
function HitComponent({ hit, sendEvent }: { hit: any; sendEvent: Function }) {
return (
<article onClick={() => sendEvent('click', hit, 'Product Clicked')}>
<h3>{hit.name}</h3>
</article>
);
}
Dashboard Metrics
Algolia's dashboard shows:
- Top searches — what users search for most
- No-result searches — queries that return nothing (add synonyms or content)
- Click-through rate — are users finding what they need?
- Click position — do users click result #1 or scroll to #10?
Pricing
| Tier | Searches/Month | Records | Price |
|---|---|---|---|
| Free | 10,000 | 10,000 | $0 |
| Build | 10,000 | 100,000 | $0 (included) |
| Grow | 10,000 | 100,000 | From $0 (usage-based) |
| Premium | Custom | Unlimited | Custom |
Overage: $1 per additional 1,000 search requests.
Common Mistakes
| Mistake | Impact | Fix |
|---|---|---|
| Indexing too many fields | Slow search, higher costs | Only index searchable fields |
| Not configuring searchable attributes | Poor relevance | Set searchableAttributes with priority order |
| Exposing admin key to client | Data can be deleted | Use search-only key in browser |
| Not handling empty states | Users see blank page | Show "No results" with suggestions |
| Stale index data | Search shows deleted/outdated items | Sync on create/update/delete |
Adding search to your app? Compare Algolia vs Meilisearch vs Typesense on APIScout — pricing, performance, and ease of integration.