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> </> );}<script setup lang="ts">import { computed, onWatcherCleanup, ref, shallowRef, watchEffect } from 'vue';import { FuzzyObjectIndex } from 'rapid-fuzzy/objects';
interface User { name: string; email: string;}
const props = defineProps<{ users: User[] }>();const query = ref('');
// Rebuild the index when the users change, and free the previous one// (and the last one on unmount).const index = shallowRef<FuzzyObjectIndex<User>>();watchEffect(() => { const built = new FuzzyObjectIndex(props.users, { keys: [{ name: 'name', weight: 2 }, 'email'], }); index.value = built; onWatcherCleanup(() => built.destroy());});
const results = computed(() => query.value && index.value ? index.value.search(query.value, 20).map((r) => r.item) : props.users,);</script>
<template> <input v-model="query" placeholder="Search…" /> <ul> <li v-for="u in results" :key="u.email">{{ u.name }}</li> </ul></template><script lang="ts"> import { FuzzyObjectIndex } from 'rapid-fuzzy/objects';
let { users }: { users: { name: string; email: string }[] } = $props(); let query = $state('');
// Rebuilt when users changes; the effect frees the previous index // (and the last one when the component is destroyed). const index = $derived( new FuzzyObjectIndex(users, { keys: [{ name: 'name', weight: 2 }, 'email'] }), ); $effect(() => { const current = index; return () => current.destroy(); });
const results = $derived(query ? index.search(query, 20).map((r) => r.item) : users);</script>
<input bind:value={query} placeholder="Search…" /><ul> {#each results as u (u.email)} <li>{u.name}</li> {/each}</ul>- Highlight matches with
highlightRanges(), imported fromrapid-fuzzy/highlightin components that only render results. - Server-side rendering: in Next.js and Vite SSR, mark
rapid-fuzzyas external on the server. See Node.js and SSR. - Large lists: pass
maxResultsand 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.