Search
Index document text, display matches, and jump to highlighted results.
Search has three parts: Search extracts the document's text, useSearch finds matches, and HighlightLayer draws a selected result. Mount Search once per viewer. Keep Pages outside it so indexing does not replace the document with a loading message.
A searchable viewer
This example searches on submit to avoid doing fuzzy matching on every keystroke. It uses the pdf-setup.ts from installation.
calculateHighlightRects uses the result's page number, match index, and searchText to locate the match. Passing the result itself keeps highlighting tied to the submitted query even if the user has since edited the input. It returns pixel rectangles in page coordinates.
Search options and results
search(text, options) is synchronous. It returns the results and also updates searchResults in that hook instance.
| Option | Default | Meaning |
|---|---|---|
threshold | 0.7 | Minimum fuzzy similarity; use a value from 0 to 1 |
limit | 10 | Result budget shared between exact and fuzzy groups |
textSize | 100 | Characters of trailing context to include after a match |
Results contain exactMatches, fuzzyMatches, and hasMoreResults. Each match has pageNumber, text, matchIndex, score, isExactMatch, and optional searchText. Matching is case-insensitive. Searching an empty or whitespace-only query clears results.
The current implementation allocates at most ceil(limit / 2) results to exact matches and the remaining budget to fuzzy matches. It can therefore return fewer than limit results even when more exact matches exist. hasMoreResults compares the total candidate count with limit; it is not a reliable count of everything omitted by the group allocation. If you build “show more,” rerun the submitted query with a higher limit rather than treating the result as paginated data.
Practical limits
Search extracts text from all pages, even though Pages only renders a viewport's worth of pages. Mount the search panel on demand for large PDFs. For search-as-you-type, debounce the query and measure with representative documents; lowering the result limit does not stop the full-document matching pass.
Image-only scans need an OCR text layer before they can be searched. PDF text extraction can also differ from visual reading order or spacing. Check the extracted text when a phrase looks present but is not found.
Live example
Exact Search Term Highlighting
This viewer highlights only the exact search term you type
Full Context Highlighting
This viewer highlights the entire text chunk containing your search term
Responsive search in long documents
useSearch() also exposes searchAsync, cancelSearch, and isSearching.
searchAsync returns the same ranked results as search, but yields between
batches so the browser can process input. It defaults to an approximate 8 ms work
budget. Scheduling can increase total search time; the benefit is responsiveness.
A newer call to either search method cancels pending async work from that hook.
Unmounting or replacing document text also cancels it. Cancelled calls reject
with AbortError and never publish partial results. You can pass an external
AbortSignal through { signal } or adjust { timeSliceMs } when needed.
Keep the component inside <Root> and use <Search> to index the document.
Indexing failures and retry
If text extraction fails, Search keeps its children unmounted and shows an error
with a Retry button. Retry starts a new indexing attempt without reloading the
viewer. To match your application's error presentation, provide a fallback:
This fragment replaces the existing Search wrapper in the example above.
The fallback also receives the original error. It handles indexing failures;
Root.onError handles document loading failures.