Search Setup

Configure MeiliSearch for powerful, typo-tolerant search in your documentation

Setting Up Search

Specra supports powerful full-text search through MeiliSearch integration. This guide shows you how to set up and configure search for your documentation.

Search Options

Specra offers two search solutions:

Local Search (Default)

  • Works out of the box
  • No external dependencies
  • Good for small to medium doc sites
  • Searches in the browser

MeiliSearch (Recommended for production)

  • Blazing fast search engine
  • Typo tolerance
  • Instant results
  • Better for large doc sites
  • Requires separate server

Quick Start with MeiliSearch

Prerequisites

  • Docker (easiest setup) or ability to run a MeiliSearch server
  • Your documentation site already set up with Specra

Installation

Run MeiliSearch with Docker

The easiest way to get MeiliSearch running locally:
Code
bash
1docker run -d \
2 --name meilisearch \
3 -p 7700:7700 \
4 -e MEILI_MASTER_KEY="aSampleMasterKey" \
5 -v $(pwd)/meili_data:/meili_data \
6 getmeili/meilisearch:latest

This starts MeiliSearch on http://localhost:7700 with a master key for authentication.

Warning
Use a strong master key in production! This example key is for development only.

Configure Specra

Update your `specra.config.json`:
Code
json
1{
2 "search": {
3 "enabled": true,
4 "provider": "meilisearch",
5 "placeholder": "Search documentation...",
6 "meilisearch": {
7 "host": "http://localhost:7700",
8 "apiKey": "aSampleMasterKey",
9 "indexName": "docs"
10 }
11 }
12}

Index Your Content

Specra includes indexing utilities. Run this from your project root:
Code
bash
1npm run index:search

This scans your docs/ folder and sends all content to MeiliSearch for indexing.

Info
Re-run the indexing command whenever you add or update documentation.

Test Search

Start your dev server:
Code
bash
1npm run dev

Press ⌘K (Mac) or Ctrl+K (Windows/Linux) to open the search modal. Start typing to see instant results!

MeiliSearch Configuration

Configuration Options

All MeiliSearch settings go in specra.config.json under the search section:

Code
json
1{
2 "search": {
3 "enabled": true,
4 "provider": "meilisearch",
5 "placeholder": "Search docs...",
6 "meilisearch": {
7 "host": "http://localhost:7700",
8 "apiKey": "your-api-key",
9 "indexName": "docs"
10 }
11 }
12}

Field reference:

FieldTypeDescription
enabledbooleanEnable/disable search feature
providerstring"meilisearch" or "local"
placeholderstringPlaceholder text in search box
meilisearch.hoststringMeiliSearch server URL
meilisearch.apiKeystringAPI key for authentication
meilisearch.indexNamestringIndex name for your docs

Search Key vs Master Key

MeiliSearch uses two types of keys:

Master Key (development)

  • Full access to all operations
  • Used for indexing content
  • Never expose in client-side code in production

Search Key (production)

  • Read-only access
  • Safe to use in browser
  • Generate from master key

Generate a search key:

Code
bash
1curl -X POST 'http://localhost:7700/keys' \
2 -H 'Authorization: Bearer aSampleMasterKey' \
3 -H 'Content-Type: application/json' \
4 --data-binary '{
5 "description": "Search key for documentation",
6 "actions": ["search"],
7 "indexes": ["docs"],
8 "expiresAt": null
9 }'

Use the returned key in your production config.

Production Deployment

Hosting MeiliSearch

For production, you need to host MeiliSearch separately from your docs. Options:

MeiliSearch Cloud (Easiest)

  • Official managed hosting
  • Starts at $0.02/hour
  • Automatic backups and updates
  • Visit meilisearch.com/cloud

Self-hosted on VPS

  • DigitalOcean, AWS, Google Cloud, etc.
  • More control, requires maintenance
  • Use Docker or binary installation

Railway / Render / Fly.io

  • One-click deploy options available
  • Good for small to medium projects

Production Configuration

Update specra.config.json for production:

Code
json
1{
2 "search": {
3 "enabled": true,
4 "provider": "meilisearch",
5 "meilisearch": {
6 "host": "https://your-meilisearch-domain.com",
7 "apiKey": "your-public-search-key",
8 "indexName": "docs"
9 }
10 }
11}
Warning
Always use a search-only API key in production, never your master key!

Indexing in CI/CD

Automate indexing when you deploy. Example GitHub Action:

Code
yaml
1name: Index Documentation
2 
3on:
4 push:
5 branches: [main]
6 paths:
7 - 'docs/**'
8 
9jobs:
10 index:
11 runs-on: ubuntu-latest
12 steps:
13 - uses: actions/checkout@v3
14 
15 - name: Setup Node.js
16 uses: actions/setup-node@v3
17 with:
18 node-version: '18'
19 
20 - name: Install dependencies
21 run: npm ci
22 
23 - name: Index documentation
24 env:
25 MEILISEARCH_HOST: ${{ secrets.MEILISEARCH_HOST }}
26 MEILISEARCH_API_KEY: ${{ secrets.MEILISEARCH_MASTER_KEY }}
27 run: npm run index:search

Store your MeiliSearch credentials as GitHub secrets.

Customizing Search Behavior

Search Settings

Configure how MeiliSearch handles searches:

Code
javascript
1// In your indexing script
2const settings = {
3 searchableAttributes: [
4 'title',
5 'description',
6 'content',
7 'headings'
8 ],
9 displayedAttributes: [
10 'title',
11 'description',
12 'url',
13 'breadcrumb'
14 ],
15 rankingRules: [
16 'words',
17 'typo',
18 'proximity',
19 'attribute',
20 'sort',
21 'exactness'
22 ],
23 stopWords: ['the', 'a', 'an'],
24 synonyms: {
25 'js': ['javascript'],
26 'ts': ['typescript']
27 }
28}

Filters

Add filters to narrow search results:

Code
json
1{
2 "search": {
3 "meilisearch": {
4 "filters": "version = 'v1.0.0'"
5 }
6 }
7}

This only shows results from a specific version.

Local Search (Alternative)

If you don't want to run MeiliSearch, use local search:

Code
json
1{
2 "search": {
3 "enabled": true,
4 "provider": "local",
5 "placeholder": "Search documentation..."
6 }
7}

Local search:

  • ✅ Works immediately, no setup
  • ✅ No external dependencies
  • ✅ Free
  • ❌ Less powerful than MeiliSearch
  • ❌ No typo tolerance
  • ❌ Slower for large doc sites

Local search is automatically built during npm run build.

Keyboard Shortcuts

Users can trigger search with:

  • ⌘K or Ctrl+K - Open search
  • Escape - Close search
  • ↑↓ - Navigate results
  • Enter - Go to selected result

These work with both local and MeiliSearch options.

Troubleshooting

Search not appearing

Check configuration:

Code
json
1{
2 "search": {
3 "enabled": true // ← Must be true
4 }
5}

Verify MeiliSearch is running:

Code
bash
1curl http://localhost:7700/health

Should return {"status":"available"}.

No search results

Re-index your content:

Code
bash
1npm run index:search

Check index exists:

Code
bash
1curl -X GET 'http://localhost:7700/indexes' \
2 -H 'Authorization: Bearer aSampleMasterKey'

CORS errors

If you get CORS errors in browser console, configure MeiliSearch to allow your domain:

Code
bash
1docker run -d \
2 --name meilisearch \
3 -p 7700:7700 \
4 -e MEILI_MASTER_KEY="aSampleMasterKey" \
5 -e MEILI_HTTP_ADDR="0.0.0.0:7700" \
6 -e MEILI_NO_ANALYTICS=true \
7 getmeili/meilisearch:latest

Or set CORS headers in your reverse proxy.

Slow indexing

For large doc sites (1000+ pages):

  • Index incrementally (only changed files)
  • Use batch requests
  • Increase MeiliSearch memory allocation
  • Run indexing in background jobs

Advanced Features

Configure language-specific stemming:

Code
javascript
1const settings = {
2 searchableAttributes: ['title', 'content'],
3 attributesForFaceting: ['lang'],
4 // ... other settings
5}
6 
7// Index with language tags
8{
9 title: "Getting Started",
10 content: "...",
11 lang: "en",
12 url: "/docs/en/getting-started"
13}

Search Analytics

Track what users search for:

Code
javascript
1// In your search component
2const logSearch = async (query, resultsCount) => {
3 await fetch('/api/analytics/search', {
4 method: 'POST',
5 body: JSON.stringify({ query, resultsCount })
6 })
7}

Use this data to improve documentation based on what users look for.

Custom Result Rendering

Override the search result component to customize how results appear:

Code
tsx
1// components/custom-search-result.tsx
2export function CustomSearchResult({ hit }) {
3 return (
4 <div className="search-result">
5 <h3>{hit.title}</h3>
6 <p>{hit.description}</p>
7 <span className="version">{hit.version}</span>
8 </div>
9 )
10}

Next Steps