Guide: React hooks¶
Vecnest provides React hooks for use in React 18+ apps. You need the vecnest/react entry and React as a peer dependency.
useVecnest¶
useVecnest(dbName) opens the IndexedDB database and returns { db, loading, error }.
import { useVecnest } from 'vecnest/react';
function MyApp() {
const { db, loading, error } = useVecnest('my-app-db');
if (loading) return <p>Loading…</p>;
if (error) return <p role="alert">Error: {error.message}</p>;
if (!db) return null;
return <div>{/* use db here */}</div>;
}
db—VecnestDBinstance (ornullwhile loading / on error).loading—truewhile the DB is opening.error—Errorif opening failed, otherwisenull.
The hook closes the DB on unmount. You do not need to call db.close() yourself.
useSearchText¶
useSearchText(db, queryText, opts) embeds the query, runs search, and returns { results, loading, error }.
import { useState } from 'react';
import { useVecnest, useSearchText } from 'vecnest/react';
function SearchUI() {
const { db, loading: dbLoading, error: dbError } = useVecnest('my-db');
const [query, setQuery] = useState('');
const { results, loading: searchLoading, error: searchError } = useSearchText(
db,
query,
{ k: 5 }
);
if (dbLoading || dbError || !db) {
return dbLoading ? <p>Loading…</p> : <p role="alert">{dbError?.message}</p>;
}
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search…"
aria-busy={searchLoading}
/>
{searchLoading && <span aria-live="polite">Searching…</span>}
{searchError && <p role="alert">{searchError.message}</p>}
<ul>
{results.map((r) => (
<li key={r.id}>
{r.metadata?.text ?? '(no text)'} — {r.score.toFixed(4)}
</li>
))}
</ul>
</div>
);
}
db— FromuseVecnest. Search runs only whendbis non-null.queryText— Query string. Results update when it changes.opts— Same assearch/searchText:k,filter,useHnsw,useWorker.
useSearch¶
useSearch(db, queryVector, opts) runs vector search (no embedding). Use it when you have a precomputed query vector.
import { useMemo } from 'react';
import { useVecnest, useSearch } from 'vecnest/react';
import { embed } from 'vecnest';
function VectorSearchUI() {
const { db } = useVecnest('my-db');
const [queryText, setQueryText] = useState('');
const queryVector = useMemo(() => {
if (!queryText.trim()) return null;
return embed(queryText); // returns Promise!
}, [queryText]);
// useSearch expects a vector. For async embedding, you'd typically
// use useSearchText instead, or manage embedding state yourself.
const { results } = useSearch(db, queryVector, { k: 5 });
// ...
}
In practice, useSearchText is usually simpler for text queries. Use useSearch when you already have a stable queryVector (e.g. from another hook or context). Keep queryVector stable (e.g. useMemo) to avoid unnecessary re-runs.
Recommended patterns¶
Guard before using db¶
Always check loading and error before using db:
if (loading) return <p>Loading…</p>;
if (error) return <p role="alert">Error: {error.message}</p>;
if (!db) return null;
Accessible loading and errors¶
- Use
aria-busyon inputs during search. - Use
aria-live="polite"for “Searching…” etc. - Use
role="alert"for errors.
Conditional rendering¶
Prefer ternaries over && for conditional UI to avoid rendering 0 or other falsy values:
{searchLoading ? <span>Searching…</span> : null}
{error ? <p role="alert">{error.message}</p> : null}
Next steps¶
- CRUD and list — Update, delete, list, count.
- Search and filters — Filtering, HNSW, and Workers.
- API reference — Full hook signatures and options.