Skip to content
Add a Custom Search Bar on the Top Bar

An image of Add a Custom Search Bar on the Top Bar

You need a custom search bar on the React PDF Viewer component’s top bar instead of using the default search tool.

  • To match your app’s design system
  • To add debounce or special search logic
  • To place the search bar directly in the top bar

You can fully control the search UI such as the input field, navigation buttons, and match counter while keeping it integrated with the React PDF Kit’s built-in search system.

The useSearchContext provides a function to search and highlight the first occurrence of a text in a React PDF.

Here are the functions we’re using for this example

NameObjective
currentMatchPositionReturn the index (1-based) of the currently highlighted match within the total search results. Help track the user’s current position among matches
loadingIndicate whether a search operation is currently in progress
nextMatchNavigates to and highlights the next search result in the document. Wraps around to the first match if the end is reached
prevMatchNavigate to and highlights the previous search result in the document. Wrap around to the last match if the beginning is reached
searchExecute a search for the specified text within the currently loaded PDF document.
setSearchUpdate the search query value without immediately executing a new search
totalMatchesIndicate the total number of text matches found in the PDF document for the current search term

After integrating the useSearchContext hook into a custom search UI, you may use createPortal to add the component into the top bar of the Viewer.

NameObjective
createPortalRender the given React content into a specific container
import { useState, useCallback, useRef, useEffect } from "react";
import {
RPConfig,
RPProvider,
RPLayout,
RPPages,
useSearchContext,
RPHorizontalBar,
RPVerticalBar
} from "@react-pdf-kit/viewer";
import React from "react";
import { createPortal } from "react-dom";
const CustomSearch = () => {
const {
search,
setSearch,
currentMatchPosition,
totalMatches,
nextMatch,
prevMatch,
loading,
} = useSearchContext();
const [searchValue, setSearchValue] = useState(search);
const handleChange = useCallback(
(e) => {
setSearchValue(e.target.value);
},
[]
);
// Debounce search input
useEffect(() => {
const timer = setTimeout(() => {
setSearch(searchValue);
}, 500);
return () => clearTimeout(timer);
}, [searchValue,searchValue]);
const handleSubmit = useCallback(() => {
setSearch(searchValue);
}, [searchValue, setSearch]);
const handleNext = useCallback(() => {
nextMatch();
}, [nextMatch]);
const handlePrev = useCallback(() => {
prevMatch();
}, [prevMatch]);
return (
<div>
<input value={searchValue} onChange={handleChange} />
<button onClick={handleSubmit}>Submit</button>
<span>
{currentMatchPosition} / {totalMatches}
</span>
<button onClick={handlePrev}>Prev</button>
<button onClick={handleNext}>Next</button>
{loading && <div>searching...</div>}
</div>
);
};
export const AppPdfViewerSearch = () => {
const ref = useRef(null);
const [target, setTarget] = useState(null);
useEffect(() => {
const elemTopBarLeft = ref.current?.querySelector('[data-rp="topBarLeft"]');
if (elemTopBarLeft) {
const wrapper = document.createElement("div");
ref.current = wrapper;
elemTopBarLeft.prepend(wrapper);
setTarget(wrapper);
}
}, []);
return (
<>
<RPConfig licenseKey="YOUR_DOMAIN_TOKEN">
<RPProvider src="https://cdn.codewithmosh.com/image/upload/v1721763853/guides/web-roadmap.pdf">
<div ref={ref}>
<RPLayout
toolbar={{
topbar: { component: <RPHorizontalBar slots={{ searchTool: false, pageNavigationTool: false }} /> }
leftSidebar: { component: <RPVerticalBar /> }
}} >
{target && createPortal(<CustomSearch />, target)}
<RPPages />
</RPLayout>
</div>
</RPProvider>
</RPConfig>
</>
);
};