A debounced, keyboard-navigable, accessible autocomplete dropdown backed by a hosted search API. Debounce, highlighting, and a11y included — you bring the styling.
Try “wireless headphons” (typo included). Keyboard arrows work too.
Debouncing, request races, keyboard navigation, accessibility, empty states — that's a week of work before you even test. SearchJet handles it all.
Firing a fetch on every keystroke hammers your API and renders stale results. A production autocomplete needs debouncing, request cancellation, and race-condition guards.
Arrow-up / arrow-down to move through suggestions, Enter to select, Escape to dismiss — building accessible combobox behavior from scratch is notoriously fiddly and a common a11y failure.
Without a server-side "no match" strategy, your users hit dead ends. A great autocomplete also suggests popular queries when there are no exact hits.
Drop-in component, or a headless hook if you want full control.
import { SearchProvider, Autocomplete } from '@searchjet/react';
function App() {
return (
<SearchProvider
indexName="docs"
searchApiKey="your_search_api_key"
>
{/* Search-as-you-type: debounced, keyboard + a11y ready */}
<Autocomplete
placeholder="Search docs, products…"
maxSuggestions={6}
debounceMs={150}
onSelect={(hit) => {
window.location.href = hit.url ?? `/product/${hit.id}`;
}}
/>
</SearchProvider>
);
}import { useState, useEffect, useCallback } from 'react';
/** Headless autocomplete — full control, zero assumptions */
function useSearchJet(value) {
const [suggestions, setSuggestions] = useState([]);
const [status, setStatus] = useState('idle');
useEffect(() => {
let cancelled = false; // race-condition guard
if (!value.trim()) {
setSuggestions([]);
setStatus('idle');
return;
}
const timer = setTimeout(async () => {
setStatus('loading');
const res = await fetch(`/api/search?q=${encodeURIComponent(value)}`);
const data = await res.json();
if (!cancelled) {
setSuggestions(data.results);
setStatus('success');
}
}, 200); // debounce
return () => {
cancelled = true; // drop stale responses
clearTimeout(timer);
};
}, [value]);
return { suggestions, status };
}
export function AutocompleteHeadless() {
const [query, setQuery] = useState('');
const [active, setActive] = useState(-1);
const { suggestions } = useSearchJet(query);
const onKeyDown = (e) => {
if (e.key === 'ArrowDown') setActive((a) => (a + 1) % suggestions.length);
if (e.key === 'ArrowUp') setActive((a) => (a <= 0 ? suggestions.length - 1 : a - 1));
if (e.key === 'Enter' && active > -1) {
const hit = suggestions[active];
window.location.href = hit.url;
}
};
return (
<input
onChange={(e) => { setQuery(e.target.value); setActive(-1); }}
onKeyDown={onKeyDown}
role="combobox"
aria-expanded={suggestions.length > 0}
placeholder="Type to search…"
/>
);
}Generate a key on the JavaScript SDK page — the widget injects it straight into your clipboard-ready code.
One npm install. The package ships typed Autocomplete, SearchBox, RefinementList, and Hits components.
Debounced by default. Set maxSuggestions, placeholder, and an onSelect handler for routing.
Styling via CSS variables, dark mode support, full keyboard + screen-reader support included. Done in minutes.
Free forever plan. 10,000 searches/month. No credit card.