Built-in Search
SmartDocs includes a fast, offline search bar powered by Fuse.js — a lightweight fuzzy search library.
How It Works
The search bar (Cmd+K / Ctrl+K) runs entirely in your browser. No server calls, no API costs, no latency.
The Index
Search uses the same page-index.json that powers the AI chat. One build artifact, two features. Each page entry contains:
{
"slug": "/guides/configuration",
"title": "Configuration Guide",
"headings": ["Environment Variables", "Providers", "Security"],
"keywords": ["smartdocs", "api", "environment", "variables"],
"description": "SmartDocs is configured through environment..."
}
Fuzzy Matching
Fuse.js uses a modified Bitap algorithm for approximate string matching. It scores pages against your query across multiple fields with different weights:
| Field | Weight | Why |
|---|---|---|
| title | 3x | Most important — page name is the primary signal |
| headings | 2x | Section titles are strong indicators of content |
| keywords | 1x | Extracted terms from page body |
| description | 0.5x | Supporting signal, least specific |
The threshold: 0.4 setting determines how strict the matching is. Lower = more strict (only very close matches), higher = more forgiving (fuzzier matches). 0.4 means "quikstart" finds "Quickstart" but "deployment" won't match "Getting Started".
Instant Results
Results appear as you type — no debounce, no loading spinners. The page index is pre-loaded when you first open search (<100ms for typical docs). Subsequent searches are instant.
Keyboard Navigation
| Key | Action |
|---|---|
| Ctrl+K / Cmd+K | Toggle search |
| ↑ ↓ | Navigate results |
| Enter | Go to selected page |
| Escape | Close search |
Why Fuse.js?
| Library | Size | Offline | Fuzzy | Speed |
|---|---|---|---|---|
| Fuse.js | 13KB gzipped | ✅ | ✅ | ⚡ Instant |
| Lunr.js | 25KB | ✅ | ❌ (stemming only) | Fast |
| Algolia | External SaaS | ❌ | ✅ | Fast (API) |
| Meilisearch | External service | ❌ | ✅ | Fast (API) |
Fuse.js was chosen because:
- Zero infrastructure — runs in the browser, no server needed
- Fuzzy matching — handles typos, partial words, approximate queries
- Tiny footprint — 13KB gzipped, loaded once
- Configurable — per-field weights, threshold tuning
- No API key — no external dependency, no cost, no privacy concern
Customizing Search
Adding More Fields
To index additional page metadata, modify the page-index.json generation in scripts/build-graph.js and add the field to Fuse.js keys in components/SearchBar.tsx.
Adjusting Sensitivity
Change the threshold value in SearchBar.tsx:
0.2— very strict, exact matches only0.4— balanced (default)0.6— very lenient, many results
Excluding Pages
Filter the pages array before passing to Fuse.js. For example, to exclude draft pages:
const published = pageIndex.pages.filter(p => !p.slug.startsWith('/drafts'));
fuseInstance = new Fuse(published, { ... });
Related Pages
- How the AI Works — The full retrieval pipeline
- Karpathy's LLM Wiki — The inspiration behind the graph approach
- Configuration Guide — Setting up your project