---
title: "EmDash Full-Text Search: Setup and Usage"
description: "How EmDash's built-in search works. LiveSearch component, programmatic search API, and searchable field configuration."
article_type: child
canonical: https://dashstro.com/learn/emdash-full-text-search
---
Search is one of those features that sounds simple until you actually have to build it. Index the content, run queries, highlight matches, keep it fast. In practice that's a non-trivial amount of infrastructure. EmDash ships it out of the box via SQLite FTS5 — no third-party service, no index sync jobs, no extra cost.

Here's everything you need to go from zero to a working search experience on your EmDash site.

## Step 1: Enable Search on a Collection

Collections opt into search in `seed.json` by adding `"search"` to their `supports` array. Individual fields then opt into indexing via `"searchable": true`.

```json
{
  "collections": [
    {
      "name": "posts",
      "label": "Posts",
      "supports": ["drafts", "revisions", "search", "seo"],
      "fields": [
        {
          "slug": "title",
          "label": "Title",
          "type": "string",
          "searchable": true
        },
        {
          "slug": "content",
          "label": "Content",
          "type": "portableText",
          "searchable": true
        },
        {
          "slug": "excerpt",
          "label": "Excerpt",
          "type": "text",
          "searchable": false
        }
      ]
    }
  ]
}
```

Only fields marked `searchable: true` are indexed. For portableText fields, EmDash extracts plain text from the block array automatically before indexing.

## Step 2: Add the LiveSearch Component

The `LiveSearch` component is a React island. Import it from `emdash/ui/search` and drop it anywhere in your layout. It ships with keyboard support (Cmd+K to open) and snippet highlighting out of the box.

```astro
---
import LiveSearch from 'emdash/ui/search';
---

<!-- Place in your header or wherever search should appear -->
<LiveSearch
  client:load
  placeholder="Search articles..."
  collections={['posts', 'docs']}
/>
```

The `collections` prop scopes results to the collections you specify. Omit it to search across all search-enabled collections. The component manages its own open/close state and renders into a modal overlay.

## Programmatic Search with search()

For server-side search — custom results pages, RSS feeds filtered by query, API routes — use the `search()` function imported from `emdash`.

```typescript
import { search } from 'emdash';

const query = Astro.url.searchParams.get('q') ?? '';

const { results, cacheHint } = await search(query, {
  collections: ['posts'],
  limit: 20,
});

// Apply cache hint so invalidation works on re-publish
Astro.cache.set(cacheHint);
```

## Search Result Shape

Each result in the array has the following fields. The `snippet` field is the most useful for display — it contains the matched passage with matching terms wrapped in `<mark>` tags.

| Field | Type | Description |
| --- | --- | --- |
| collection | string | Slug of the collection this result belongs to |
| id | string (ULID) | Database ID of the matching entry |
| title | string | Title field value of the matching entry |
| slug | string | URL slug for linking to the matching entry |
| snippet | string (HTML) | Matched passage with <mark> highlights around matched terms |
| score | number | FTS5 relevance score (higher = more relevant) |

## Cmd+K Keyboard Shortcut

The `LiveSearch` component registers a Cmd+K (Mac) / Ctrl+K (Windows/Linux) listener automatically when rendered with `client:load`. No additional setup needed. The shortcut opens the search modal and focuses the input.

## CSS Theming

The search UI is styled via CSS custom properties. Override them in your `global.css` to match your design system. Key variables include `--emdash-search-bg`, `--emdash-search-border`, `--emdash-search-highlight`, and `--emdash-search-text`. The modal overlay color is `--emdash-search-overlay`.

:::pullquote
SQLite FTS5 means search is co-located with your content. No sync lag, no API quota, no third-party dependency to manage. It just works.
:::

With search enabled and LiveSearch dropped into your layout, you have a fully functional search experience in under 10 minutes. The programmatic API gives you everything you need for custom results pages and filtered feeds on top of the same index.

[Read: Build Your First EmDash Website](/learn/build-first-emdash-website)

