Skip to main content

React Hooks

The Seekora UI SDK provides several hooks for building custom search experiences.

useSeekoraSearch

Hook for performing searches with the Seekora client.

Signature

function useSeekoraSearch(options: {
client: SeekoraClient;
autoTrack?: boolean;
}): {
search: (query: string, options?: SearchOptions) => Promise<SearchResponse>;
results: SearchResponse | null;
loading: boolean;
error: Error | null;
context: SearchContext | null;
clearResults: () => void;
};

Options

OptionTypeDefaultDescription
clientSeekoraClientRequiredSeekora client instance
autoTrackbooleantrueAuto-track search events

Returns

PropertyTypeDescription
searchfunctionPerform search function
resultsSearchResponse | nullSearch results
loadingbooleanLoading state
errorError | nullError state
contextSearchContext | nullSearch context for analytics
clearResultsfunctionClear results function

Example

import { useSeekoraSearch } from '@seekora-ai/ui-sdk-react';

function CustomSearch() {
const { search, results, loading, error, clearResults } = useSeekoraSearch({
client,
autoTrack: true,
});

const handleSubmit = async (query: string) => {
await search(query, {
per_page: 20,
facet_by: 'category,brand',
});
};

if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;

return (
<div>
{results?.results.map((result) => (
<div key={result.id}>{result.title}</div>
))}
</div>
);
}

useQuerySuggestions

Hook for fetching query suggestions with debouncing.

Signature

function useQuerySuggestions(options: {
client: SeekoraClient;
query: string;
enabled?: boolean;
debounceMs?: number;
maxSuggestions?: number;
options?: QuerySuggestionsOptions;
}): {
suggestions: string[];
loading: boolean;
error: Error | null;
};

Options

OptionTypeDefaultDescription
clientSeekoraClientRequiredSeekora client
querystringRequiredCurrent query
enabledbooleantrueEnable fetching
debounceMsnumber300Debounce delay
maxSuggestionsnumber10Max suggestions
optionsQuerySuggestionsOptions-Additional options

Example

import { useState } from 'react';
import { useQuerySuggestions } from '@seekora-ai/ui-sdk-react';

function Autocomplete() {
const [query, setQuery] = useState('');
const [showSuggestions, setShowSuggestions] = useState(false);

const { suggestions, loading } = useQuerySuggestions({
client,
query,
enabled: query.length >= 2 && showSuggestions,
debounceMs: 300,
maxSuggestions: 8,
});

return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
onFocus={() => setShowSuggestions(true)}
onBlur={() => setTimeout(() => setShowSuggestions(false), 200)}
/>
{showSuggestions && suggestions.length > 0 && (
<ul className="suggestions">
{loading ? (
<li>Loading...</li>
) : (
suggestions.map((suggestion, i) => (
<li
key={i}
onClick={() => {
setQuery(suggestion);
setShowSuggestions(false);
}}
>
{suggestion}
</li>
))
)}
</ul>
)}
</div>
);
}

useSearchState

Hook for accessing and managing search state.

Signature

function useSearchState(): {
// Query
query: string;
setQuery: (query: string) => void;

// Results
results: SearchResponse | null;
loading: boolean;
error: Error | null;

// Pagination
currentPage: number;
setPage: (page: number) => void;
totalPages: number;

// Items per page
itemsPerPage: number;
setItemsPerPage: (count: number) => void;

// Refinements
refinements: Refinement[];
addRefinement: (field: string, value: string) => void;
removeRefinement: (field: string, value: string) => void;
clearRefinements: () => void;
hasRefinement: (field: string, value: string) => boolean;

// Sorting
sortBy: string;
setSortBy: (value: string) => void;
};

Example

import { useSearchState } from '@seekora-ai/ui-sdk-react';

function SearchControls() {
const {
query,
setQuery,
results,
currentPage,
setPage,
totalPages,
refinements,
addRefinement,
removeRefinement,
clearRefinements,
sortBy,
setSortBy,
} = useSearchState();

return (
<div>
{/* Query */}
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>

{/* Active Refinements */}
<div className="refinements">
{refinements.map((ref) => (
<span key={`${ref.field}-${ref.value}`}>
{ref.field}: {ref.value}
<button onClick={() => removeRefinement(ref.field, ref.value)}>
×
</button>
</span>
))}
{refinements.length > 0 && (
<button onClick={clearRefinements}>Clear all</button>
)}
</div>

{/* Sort */}
<select value={sortBy} onChange={(e) => setSortBy(e.target.value)}>
<option value="relevance:desc">Relevance</option>
<option value="price:asc">Price: Low to High</option>
<option value="price:desc">Price: High to Low</option>
</select>

{/* Results Count */}
<p>{results?.totalResults ?? 0} results</p>

{/* Pagination */}
<div className="pagination">
<button
disabled={currentPage === 1}
onClick={() => setPage(currentPage - 1)}
>
Previous
</button>
<span>
Page {currentPage} of {totalPages}
</span>
<button
disabled={currentPage === totalPages}
onClick={() => setPage(currentPage + 1)}
>
Next
</button>
</div>
</div>
);
}

useAnalytics

Hook for tracking analytics events. Requires a Seekora client (from context or passed in options).

Signature

function useAnalytics(options: {
client: SeekoraClient;
enabled?: boolean;
}): {
trackEvent: (eventType: string, payload: Partial<DataTypesEventPayload>, context?: SearchContext) => Promise<void>;
trackClick: (resultId: string, result: any, context?: SearchContext, position?: number) => Promise<void>;
trackConversion: (resultId: string, result: any, value?: number, currency?: string, context?: SearchContext) => Promise<void>;
trackBatch: (events: (Partial<DataTypesEventPayload> & { event_name?: string })[], context?: SearchContext) => Promise<void>;
};

Example

When used inside SearchProvider, get client from useSearchContext():

import { useAnalytics, useSearchContext, useSearchState } from '@seekora-ai/ui-sdk-react';

function ProductCard({ product, index }) {
const { client } = useSearchContext();
const { trackClick, trackConversion } = useAnalytics({ client });
const { results } = useSearchState();

const handleClick = async () => {
await trackClick(product.id, product, results?.context ?? undefined, index + 1);
navigate(`/product/${product.id}`);
};

const handleAddToCart = async () => {
await trackConversion(product.id, product, product.price, 'USD', results?.context ?? undefined);
addToCart(product);
};

return (
<div className="product-card" onClick={handleClick}>
<img src={product.image} alt={product.title} />
<h3>{product.title}</h3>
<p>${product.price}</p>
<button onClick={handleAddToCart}>Add to Cart</button>
</div>
);
}

useQuerySuggestionsEnhanced

Enhanced hook with keyboard navigation support.

Signature

function useQuerySuggestionsEnhanced(options: {
client: SeekoraClient;
query: string;
onSelect?: (suggestion: string) => void;
enabled?: boolean;
}): {
suggestions: string[];
loading: boolean;
error: Error | null;
selectedIndex: number;
setSelectedIndex: (index: number) => void;
handleKeyDown: (e: KeyboardEvent) => void;
selectCurrent: () => void;
};

Example

import { useState, useRef } from 'react';
import { useQuerySuggestionsEnhanced } from '@seekora-ai/ui-sdk-react';

function EnhancedAutocomplete() {
const [query, setQuery] = useState('');
const inputRef = useRef<HTMLInputElement>(null);

const {
suggestions,
loading,
selectedIndex,
handleKeyDown,
} = useQuerySuggestionsEnhanced({
client,
query,
onSelect: (suggestion) => {
setQuery(suggestion);
performSearch(suggestion);
},
});

return (
<div>
<input
ref={inputRef}
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={handleKeyDown}
/>
{suggestions.length > 0 && (
<ul>
{suggestions.map((suggestion, i) => (
<li
key={i}
className={i === selectedIndex ? 'selected' : ''}
>
{suggestion}
</li>
))}
</ul>
)}
</div>
);
}

useSmartSuggestions

Hook with caching and smart prefetching.

Signature

function useSmartSuggestions(options: {
client: SeekoraClient;
query: string;
cacheTime?: number;
prefetch?: boolean;
}): {
suggestions: string[];
loading: boolean;
error: Error | null;
cached: boolean;
};

Example

import { useSmartSuggestions } from '@seekora-ai/ui-sdk-react';

function SmartAutocomplete() {
const [query, setQuery] = useState('');

const { suggestions, loading, cached } = useSmartSuggestions({
client,
query,
cacheTime: 60000, // Cache for 1 minute
prefetch: true,
});

return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
{loading && !cached && <span>Loading...</span>}
{suggestions.map((s, i) => (
<div key={i}>{s}</div>
))}
</div>
);
}

useSuggestionsAnalytics

Hook for tracking suggestion analytics.

Signature

function useSuggestionsAnalytics(options?: {
client?: SeekoraClient;
}): {
trackSuggestionView: (suggestions: string[]) => void;
trackSuggestionClick: (suggestion: string, position: number) => void;
};

Example

import { useSuggestionsAnalytics } from '@seekora-ai/ui-sdk-react';

function AnalyticsAwareSuggestions({ suggestions }) {
const { trackSuggestionView, trackSuggestionClick } = useSuggestionsAnalytics();

useEffect(() => {
if (suggestions.length > 0) {
trackSuggestionView(suggestions);
}
}, [suggestions]);

return (
<ul>
{suggestions.map((suggestion, i) => (
<li
key={i}
onClick={() => {
trackSuggestionClick(suggestion, i + 1);
selectSuggestion(suggestion);
}}
>
{suggestion}
</li>
))}
</ul>
);
}

Custom Hook Examples

useDebounce

function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);

useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);

return debouncedValue;
}

// Usage with search
function DebouncedSearch() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 300);
const { search, results } = useSeekoraSearch({ client });

useEffect(() => {
if (debouncedQuery) {
search(debouncedQuery);
}
}, [debouncedQuery]);

return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
);
}

useInfiniteSearch

function useInfiniteSearch(options: { client: SeekoraClient }) {
const [results, setResults] = useState<any[]>([]);
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const [loading, setLoading] = useState(false);

const loadMore = async (query: string) => {
if (loading || !hasMore) return;

setLoading(true);
const response = await options.client.search(query, {
page,
per_page: 20,
});

setResults((prev) => [...prev, ...response.results]);
setHasMore(response.results.length === 20);
setPage((p) => p + 1);
setLoading(false);
};

const reset = () => {
setResults([]);
setPage(1);
setHasMore(true);
};

return { results, loadMore, hasMore, loading, reset };
}

Next Steps