Generated Index Fields
The plugin writes one entry per page to/search-index.json. Each entry has this shape:
{
"id": "page:/blog/post",
"type": "page",
"url": "/blog/post",
"title": "Page Title",
"content": "All page text extracted from rendered HTML...",
"excerpt": "Auto-generated 250-character excerpt...",
"headings": [
{ "level": "h2", "id": "introduction", "title": "Introduction" },
{ "level": "h3", "id": "overview", "title": "Overview" }
],
"wordCount": 1523
}
The full JSON file also includes top-level metadata:version,generated,totalEntries,stats, andconfig.fuseOptions(so clients can reconstruct the same Fuse configuration if they want).
Client-Side Fuse.js Configuration
The search partial (search.js) applies its own weights on top of the loaded index:
const fuseOptions = {
keys: [
{ name: 'title', weight: 10 },
{ name: 'headings.title', weight: 7 },
{ name: 'content', weight: 5 },
{ name: 'excerpt', weight: 3 }
],
threshold: 0.4,
includeScore: true,
includeMatches: true,
minMatchCharLength: 3,
ignoreLocation: true
};
When a match falls inside a heading's title, the client appends#<headingId>to the result URL so users land on the correct section.
Performance Optimization
Two-Stage Relevance Filtering (insearch.js):
- Fuse returns fuzzy matches above threshold
0.4 - Client then drops any result below 50% relevance (
(1 - score) * 100 < 50) - Client also requires an exact case-insensitive substring match in title, content, excerpt, or a heading — this removes typical fuzzy-search false positives
Dynamic Library Loading:
- Fuse.js is loaded from jsDelivr CDN only when the search component renders
- The loader caches its promise so multiple search instances on a page share one network request