Skip to content

UI frameworks

Build the index once per data set and search it on every keystroke. destroy() frees an index’s memory right away; an index you simply drop is freed by the garbage collector. In the browser, bundlers load the WebAssembly build automatically (see Browsers and bundlers).

import { useMemo, useState } from 'react';
import { FuzzyObjectIndex } from 'rapid-fuzzy/objects';
interface User {
name: string;
email: string;
}
export function UserSearch({ users }: { users: User[] }) {
const [query, setQuery] = useState('');
// Rebuild the index only when the data changes. The garbage collector
// frees an index that is no longer referenced, so no cleanup is needed
// (calling destroy() in an effect cleanup would break under StrictMode,
// which runs effects twice on the same memoized index).
const index = useMemo(
() => new FuzzyObjectIndex(users, { keys: [{ name: 'name', weight: 2 }, 'email'] }),
[users],
);
const results = query ? index.search(query, 20).map((r) => r.item) : users;
return (
<>
<input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Search…" />
<ul>
{results.map((u) => (
<li key={u.email}>{u.name}</li>
))}
</ul>
</>
);
}
  • Highlight matches with highlightRanges(), imported from rapid-fuzzy/highlight in components that only render results.
  • Server-side rendering: in Next.js and Vite SSR, mark rapid-fuzzy as external on the server. See Node.js and SSR.
  • Large lists: pass maxResults and render only the results you show. A search over 100,000 items takes about a millisecond with the native addon; the WebAssembly build in the browser is slower and single-threaded, so consider a Web Worker for very large lists.