Semantic search

Search by meaning over the app's own records with a local vector index and hybrid keyword matching, showing why each result matched.

Mock· scripted answersNo model runs in a browser, so every answer here is scripted. On a phone it comes from the model.
  • Apple Foundation Models: supported
  • Gemini Nano: not supported
  • Llama (not yet supported): not supported
  • A cloud model: supported
  • Embeddings come from Apple (NLContextualEmbedding) or a cloud model. Without them it falls back to keyword search.

Install

npx ondai add semantic-search

Usage

import { SemanticSearch } from '@/components/ondai/semantic-search'
import { sqliteStorage } from '@ondai/core/expo'
import { openDatabaseAsync } from 'expo-sqlite'

<SemanticSearch
  items={notes.map((n) => ({ id: n.id, text: n.body, metadata: n }))}
  storage={sqliteStorage({ open: openDatabaseAsync })}
  presentation="bar" resultStyle="list"
  onSelect={(hit) => router.push(`/notes/${hit.id}`)}
/>
  • Embeddings come from Apple NLContextualEmbedding (512 dims, iOS 17+) or a cloud embedding model; without them, keyword search still works.
  • Hybrid scoring blends cosine similarity and keyword overlap; "why it matched" shows the terms.

Customize

Every AI component takes the same look and customization props, so it fits your app rather than ours.

<SemanticSearch
  variant="pop"
  tint="#FF5A5F"
  size="lg"
  radius={12}
  icons={{ search: Search, clear: null }}
  labels={{ placeholder: '…', title: '…' }}
/>
PropWhat it does
variantThe look of the whole component: clean, minimal, glass, pop, candy, metal, shine
sizesm, md, lg; type, rows, icons and keys scale together
tintAny colour; the variant paints its keys, tiles and glow in it
radiusCorner radius of the container
iconsReplace any icon (a lucide component, an element, or null to hide it): search, clear, result, recent, empty, error
labelsEvery visible and spoken string; defaults are exported as semanticSearchLabels: placeholder, title, recent, searching, indexing, of, items, emptyTitle, emptyHint, matches, similar, result, results, byMeaning, keywordsOnly, other, clear, inputLabel
SlotsDraw a part yourself: renderIcon, renderItem, renderResult, renderEmpty, renderFooter
stylesStyle any named part

Variants

PropOptions
presentationbar, spotlight, header
variantclean, minimal, glass, pop, candy, metal, shine
resultStylelist, cards, grouped
sizesm, md, lg

Token budget

Input budget: 512 tokens. Embeddings only (Apple NLContextualEmbedding, 512 dims); no LLM call. Keyword fallback without embeddings.

Fallback

When no provider is ready the component renders the unavailable state (AvailabilityGate copy) and, where a deterministic path exists, keeps working without the model.

Eval score

No model-driven fixtures for this component.

Files

Registry dependencies: model-download, on-device-badge, sheet, shimmer-text, surface, skeleton

npm dependencies: @ondai/core, @ondai/tokens, expo-haptics, lucide-react-native, react-native-reanimated

On this page