Skip to content

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>;
}
  • dbVecnestDB instance (or null while loading / on error).
  • loadingtrue while the DB is opening.
  • errorError if opening failed, otherwise null.

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 — From useVecnest. Search runs only when db is non-null.
  • queryText — Query string. Results update when it changes.
  • opts — Same as search / 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.


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-busy on 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